Skip to content

The name mangling between x64 and arm64ec is inconsistent. #166140

@scout-zeng

Description

@scout-zeng

We are trying to compile V8 as a dynamic library targeting the Arm64EC architecture using clang-cl, and then replace the x64 version of the V8 library in node.exe with this Arm64EC version. However, We found that the name mangling between the x64 version and the Arm64EC version differs in some cases. For the following code snippet.

TimeBase : public TimeConstants {
 public:
#if V8_OS_WIN
  // To avoid overflow in QPC to Microseconds calculations, since we multiply
  // by kMicrosecondsPerSecond, then the QPC value should not exceed
  // (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply.
  static constexpr int64_t kQPCOverflowThreshold = INT64_C(0x8637BD05AF7);
#endif

  // Returns true if this object has not been initialized.
  //
  // Warning: Be careful when writing code that performs math on time values,
  // since it's possible to produce a valid "zero" result that should not be
  // interpreted as a "null" value.
  constexpr bool IsNull() const { return us_ == 0; }

  // Returns the maximum/minimum times, which should be greater/less than any
  // reasonable time with which we might compare it.
  static TimeClass Max() {
    return TimeClass(std::numeric_limits<int64_t>::max());
  }
  static TimeClass Min() {
    return TimeClass(std::numeric_limits<int64_t>::min());
  }

  // Returns true if this object represents the maximum/minimum time.
  constexpr bool IsMax() const {
    return us_ == std::numeric_limits<int64_t>::max();
  }
  constexpr bool IsMin() const {
    return us_ == std::numeric_limits<int64_t>::min();
  }

  // For serializing only. Use FromInternalValue() to reconstitute. Please don't
  // use this and do arithmetic on it, as it is more error prone than using the
  // provided operators.
  int64_t ToInternalValue() const { return us_; }

  // The amount of time since the origin (or "zero") point. This is a syntactic
  // convenience to aid in code readability, mainly for debugging/testing use
  // cases.
  //
  // Warning: While the Time subclass has a fixed origin point, the origin for
  // the other subclasses can vary each time the application is restarted.
  constexpr TimeDelta since_origin() const {
    return TimeDelta::FromMicroseconds(us_);
  }

  TimeClass& operator=(TimeClass other) {
    us_ = other.us_;
    return *(static_cast<TimeClass*>(this));
  }

  // Compute the difference between two times.
  TimeDelta operator-(TimeClass other) const {
    return TimeDelta::FromMicroseconds(us_ - other.us_);
  }

  // Return a new time modified by some delta.
  TimeClass operator+(TimeDelta delta) const {
    return TimeClass(bits::SignedSaturatedAdd64(delta.delta_, us_));
  }
  TimeClass operator-(TimeDelta delta) const {
    return TimeClass(-bits::SignedSaturatedSub64(delta.delta_, us_));
  }

  // Modify by some time delta.
  TimeClass& operator+=(TimeDelta delta) {
    return static_cast<TimeClass&>(*this = (*this + delta));
  }
  TimeClass& operator-=(TimeDelta delta) {
    return static_cast<TimeClass&>(*this = (*this - delta));
  }

  constexpr auto operator<=>(const TimeBase&) const = default;

  // Converts an integer value representing TimeClass to a class. This is used
  // when deserializing a |TimeClass| structure, using a value known to be
  // compatible. It is not provided as a constructor because the integer type
  // may be unclear from the perspective of a caller.
  static TimeClass FromInternalValue(int64_t us) { return TimeClass(us); }

 protected:
  explicit constexpr TimeBase(int64_t us) : us_(us) {}

  // Time value in a microsecond timebase.
  int64_t us_;
};

What you should pay attention to is the constexpr auto operator<=>(const TimeBase&) const = default;
When I compiled with Clang-cl 21.1.3, the generated symbol is
2B6 00000000 SECTDC notype () External | ??__M?$TimeBase@VTimeTicks@base@v8@@@time_internal@base@v8@@QEBA?A?<auto>@@AEBV0123@@Z (public: <auto> __cdecl v8::base::time_internal::TimeBase<class v8::base::TimeTicks>::operator<=>(class v8::base::time_internal::TimeBase<class v8::base::TimeTicks> const &)const )
However, when i compiled with Clang 21.0.0, the generated symbols is
26C 00000000 SECTC6 notype () External | ??__M?$TimeBase@VTimeTicks@base@v8@@@time_internal@base@v8@@$$hQEBA@AEBV0123@@Z (public: __cdecl v8::base::time_internal::TimeBase<class v8::base::TimeTicks>::operator<=>(class v8::base::time_internal::TimeBase<class v8::base::TimeTicks> const &)const )

From the perspective of MSVC name mangling, the difference between the two symbols lies in the return type. However, I'm not sure whether this is caused by subtle differences between compiler versions or by specific compiler options. I'd appreciate any suggestions or insights.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"questionA question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions