Skip to content

[WebAssembly] Explicitly set getCmpLibcallReturnType to i32#192425

Open
tgross35 wants to merge 1 commit intollvm:mainfrom
tgross35:wasm-cmp-update
Open

[WebAssembly] Explicitly set getCmpLibcallReturnType to i32#192425
tgross35 wants to merge 1 commit intollvm:mainfrom
tgross35:wasm-cmp-update

Conversation

@tgross35
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 commented Apr 16, 2026

LLVM's default for getCmpLibcallReturnType is currently set to i32,
but this is not exactly accurate: the default in GCC is an integer of
word size. This means that on wasm64 targets, runtime libraries that are
assuming word size are incorrect and instead need to be using a 32-bit
integer.

The unintentional i32 default is actually a reasonable choice for Wasm
because it may be slightly cheaper to operate on when running 64-bit
wasm on 32-bit hosts, and there is no advantage to using a larger
integer (the result of comparisons need only be able to represent three
possible values). Thus, make the i32 explicit.

GCC does not support wasm64 so there is no compatibility concern at the
current time. If added, it would be reasonable for i32 to be used
there as well.

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Apr 16, 2026

@llvm/pr-subscribers-backend-webassembly

Author: Trevor Gross (tgross35)

Changes

LLVM's default for getCmpLibcallReturnType is currently set to i32, but this is not exactly accurate: the default in GCC is an integer of word size. This means that on wasm64 targets, runtime libraries that are assuming word size are incorrect and instead need to be using a 32-bit integer.

The unintentional i32 default is actually a reasonable choice for Wasm because it may be slightly cheaper to operate on when running 64-bit wasm on 32-bit hosts, and there is no advantage to using a larger integer (the result of comparisons need only be able to represent three possible values). Thus, do the following:

  • Make getCmpLibcallReturnType explicit as i32 to prevent against future changes to the default.
  • Update compiler-rt to account for this and fix wasm64.
  • Note this decision in the changelog so other runtime libraries can update if needed.

GCC does not support wasm64 so there is no compatibility concern at the current time. If added, it would be reasonable for i32 to be used there as well.

Closes: #75302
Closes: #192416


Full diff: https://github.com/llvm/llvm-project/pull/192425.diff

3 Files Affected:

  • (modified) compiler-rt/lib/builtins/fp_compare_impl.inc (+3)
  • (modified) llvm/docs/ReleaseNotes.md (+4-1)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h (+4)
diff --git a/compiler-rt/lib/builtins/fp_compare_impl.inc b/compiler-rt/lib/builtins/fp_compare_impl.inc
index f883338c471d3..129d0136a0b98 100644
--- a/compiler-rt/lib/builtins/fp_compare_impl.inc
+++ b/compiler-rt/lib/builtins/fp_compare_impl.inc
@@ -15,6 +15,9 @@
 #if defined(__aarch64__) || defined(__arm64ec__)
 // AArch64 GCC overrides libgcc_cmp_return to use int instead of long.
 typedef int CMP_RESULT;
+#elif defined(__wasm__)
+// Both wasm32 and wasm64 use i32
+typedef int CMP_RESULT;
 #elif __SIZEOF_POINTER__ == 8 && __SIZEOF_LONG__ == 4
 // LLP64 ABIs use long long instead of long.
 typedef long long CMP_RESULT;
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 9caeef89d0b1c..14958f5674d46 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -106,7 +106,7 @@ Changes to LLVM infrastructure
   ([#177046](https://github.com/llvm/llvm-project/pull/125687)). Note that
   there are still issues with invalid compiler reasoning about some functions
   in bitcode, e.g. `malloc`. Not yet supported on MachO or when using
-  distributed ThinLTO. 
+  distributed ThinLTO.
 
 Changes to building LLVM
 ------------------------
@@ -198,6 +198,9 @@ Changes to the RISC-V Backend
 Changes to the WebAssembly Backend
 ----------------------------------
 
+* The result of runtime library call comparisons is explicitly set to `i32` on
+  both wasm32 and wasm64.
+
 Changes to the Windows Target
 -----------------------------
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 42f047840e504..5de57615dccd4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -29,6 +29,10 @@ class WebAssemblyTargetLowering final : public TargetLowering {
   MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override;
   MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const override;
 
+  MVT::SimpleValueType getCmpLibcallReturnType() const override {
+    // i32 may be more efficient than the default word size for wasm64 targets
+    return MVT::i32;
+  }
 private:
   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
   /// right decision when generating code for different targets.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 16, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.


MVT::SimpleValueType getCmpLibcallReturnType() const override {
// i32 may be more efficient than the default word size for wasm64 targets
return MVT::i32;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But doesn't the base class already default to returning i32? i.e. this override isn't doing anything is it?

See:

MVT::SimpleValueType TargetLoweringBase::getCmpLibcallReturnType() const {
return MVT::i32; // return the default value
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently yes but that's something that should change. This is just making the wasm choice explicit before that happens since (afaik) wasm64 is the only one to hit the case where i32 and a word have an incompatible ABI, aside from those with overrides in GCC.

I'm planning to follow up with #192441

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather you include the override when you land #192441.. since that is the point at which it becomes needed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'm not actually sure its worth diverging here for wasm64. We might just want to go with wordsize too for consistency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can drop the override.

Also, I'm not actually sure its worth diverging here for wasm64. We might just want to go with wordsize too for consistency.

On most 64-bit targets there is no extra cost between a word sized comparison and a 32-bit and they share the same ABI, so those targets that happen to be consistent also aren't paying anything extra. IIUC however, wasm64 can still be running on 32-bit targets. On those, using a 64-bit comparison result rather than 32-bit means each libcall cmp costs up to (1) an extra register to be clobbered during return, (2) an extra instruction to put a meaningless value in that register, (3) ~2 extra instructions when checking the compare result.

This probably doesn't show up noticeably, especially considering the libcall is doing a lot more. But it seems like a reasonable enough case to break consistency considering 62 of the bits are wasted anyway.

Copy link
Copy Markdown
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea why this hasn't shown up already, for example, in emscripten?

@tgross35
Copy link
Copy Markdown
Contributor Author

Any idea why this hasn't shown up already, for example, in emscripten?

@Spxg pointed out in rust-lang/compiler-builtins#1199 that emscripten's fork of compiler-rt is patching for this https://github.com/emscripten-core/emscripten/blob/88e0002141254cdb772b558f63d2c1ad9b215eb2/system/lib/compiler-rt/lib/builtins/fp_compare_impl.inc#L15.

@sbc100
Copy link
Copy Markdown
Collaborator

sbc100 commented Apr 16, 2026

Any idea why this hasn't shown up already, for example, in emscripten?

@Spxg pointed out in rust-lang/compiler-builtins#1199 that emscripten's fork of compiler-rt is patching for this https://github.com/emscripten-core/emscripten/blob/88e0002141254cdb772b558f63d2c1ad9b215eb2/system/lib/compiler-rt/lib/builtins/fp_compare_impl.inc#L15.

Oh yes that makes sense :)

How about just porting emscripten patch here (i.e. just modifying line 15 instead of adding a whole new block?)

@tgross35
Copy link
Copy Markdown
Contributor Author

How about just porting emscripten patch here (i.e. just modifying line 15 instead of adding a whole new block?)

Mostly because "// AArch64 GCC overrides libgcc_cmp_return to use int instead of long." doesn't apply here

@sbc100
Copy link
Copy Markdown
Collaborator

sbc100 commented Apr 16, 2026

How about just porting emscripten patch here (i.e. just modifying line 15 instead of adding a whole new block?)

Mostly because "// AArch64 GCC overrides libgcc_cmp_return to use int instead of long." doesn't apply here

You can add anther line to the existing comment.

sbc100 added a commit that referenced this pull request Apr 20, 2026
…method. NFC (#192483)

This trivial method makes more sense in the header (indeed, both the
existing overrides for it are already in headers).

I noticed this while hunting for the default value while reviewing
#192425
@dschuff
Copy link
Copy Markdown
Member

dschuff commented Apr 20, 2026

/cc @aheejin

Copy link
Copy Markdown
Member

@dschuff dschuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a mild preference for the separate ifdef for wasm in compiler-rt since we have a separate comment, but I could go either way.

s-perron pushed a commit to s-perron/llvm-project that referenced this pull request Apr 21, 2026
…method. NFC (llvm#192483)

This trivial method makes more sense in the header (indeed, both the
existing overrides for it are already in headers).

I noticed this while hunting for the default value while reviewing
llvm#192425
@tgross35
Copy link
Copy Markdown
Contributor Author

Thanks for the reviews. I'll need one of you to merge this, unless anyone feels more strongly about the comment or deferring the override. (I have a mild preference as-is of course, but 🤷‍♂️ )

@sbc100
Copy link
Copy Markdown
Collaborator

sbc100 commented Apr 24, 2026

I thought we were doing to avoid adding an override until one is needed? I.e. until #192441 lands?

Comment thread llvm/docs/ReleaseNotes.md Outdated
----------------------------------

* The result of runtime library call comparisons is explicitly set to `i32` on
both wasm32 and wasm64.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since i32 was the default already I don't think we need to changelog entry do we? i.e. nothing actually changed here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no change, but this was meant as an FYI that forks of compiler-rt or independent implementations may want to update. That affects a very small number of people though, so I'll drop.

typedef int CMP_RESULT;
#elif defined(__wasm__)
// Both wasm32 and wasm64 use i32
typedef int CMP_RESULT;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems like it can land in isolation, it just reflects the current reality, right?

@tgross35
Copy link
Copy Markdown
Contributor Author

I thought we were doing to avoid adding an override until one is needed? I.e. until #192441 lands?

I still feel like having a target-specific change is nicer in log/blame to make it clear that this was an intentional decision, rather than being wrapped in a more generic "change default, preserve current behavior" diff. I've been bitten by this before, still wondering about some GCC ABI decisions that look like this in git :)

Also took the other approvals to think this was fine. But if you still feel strongly, will drop.

@tgross35
Copy link
Copy Markdown
Contributor Author

I think it's easier to just split off the compiler-rt changes since the context is pretty different. Opened #194093

LLVM's default for `getCmpLibcallReturnType` is currently set to `i32`,
but this is not exactly accurate: the default in GCC is an integer of
word size. This means that on wasm64 targets, runtime libraries that are
assuming word size are incorrect and instead need to be using a 32-bit
integer.

The unintentional i32 default is actually a reasonable choice for Wasm
because it may be slightly cheaper to operate on when running 64-bit
wasm on 32-bit hosts, and there is no advantage to using a larger
integer (the result of comparisons need only be able to represent three
possible values). Thus, make the `i32` explicit.

GCC does not support wasm64 so there is no compatibility concern at the
current time. If added, it would be reasonable for `i32` to be used
there as well.
KHicketts pushed a commit to KHicketts/llvm-project that referenced this pull request Apr 30, 2026
…method. NFC (llvm#192483)

This trivial method makes more sense in the header (indeed, both the
existing overrides for it are already in headers).

I noticed this while hunting for the default value while reviewing
llvm#192425
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants