[llvm] [WebAssembly] Print type signature and table for call_indirect (PR #179120)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 13 23:25:42 PST 2026
https://github.com/ParkHanbum updated https://github.com/llvm/llvm-project/pull/179120
>From 1d8dc5fd4dc506a7a34532d1cea363865b45da03 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Mon, 2 Feb 2026 00:26:29 +0900
Subject: [PATCH 01/12] [WebAssembly] Print type signature and table for
call_indirect
Update WebAssemblyInstPrinter.cpp to correctly print type
and table operands for both register and stack modes.
---
.../MCTargetDesc/WebAssemblyInstPrinter.cpp | 18 +++++++++++
.../WebAssembly/WebAssemblyInstrCall.td | 4 +--
llvm/test/CodeGen/WebAssembly/call-pic.ll | 2 +-
llvm/test/CodeGen/WebAssembly/call.ll | 30 +++++++++----------
.../CodeGen/WebAssembly/function-bitcasts.ll | 2 +-
llvm/test/CodeGen/WebAssembly/multivalue.ll | 2 +-
llvm/test/CodeGen/WebAssembly/reg-stackify.ll | 2 +-
llvm/test/CodeGen/WebAssembly/swiftcc.ll | 8 ++---
llvm/test/CodeGen/WebAssembly/tailcall.ll | 28 ++++++++---------
9 files changed, 57 insertions(+), 39 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 651f631c1ee55..f883c5175a408 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -49,6 +49,24 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
const MCSubtargetInfo &STI,
raw_ostream &OS) {
switch (MI->getOpcode()) {
+ case WebAssembly::CALL_INDIRECT:
+ case WebAssembly::RET_CALL_INDIRECT: {
+ OS << "\t" << getMnemonic(*MI).first << "\t";
+ unsigned TypeOperand = 0;
+ unsigned TableOperand = 1;
+ if (MI->getOpcode() == WebAssembly::CALL_INDIRECT) {
+ unsigned NumDefs = MI->getOperand(0).getImm();
+ TypeOperand = 1 + NumDefs; // Type 위치 보정
+ TableOperand = 1 + NumDefs + 1; // Table 위치 보정
+ }
+ printOperand(MI, TableOperand, OS);
+ OS << ", ";
+ printOperand(MI, TypeOperand, OS);
+ if (MI->getOpcode() == WebAssembly::CALL_INDIRECT) {
+ OS << ", ";
+ }
+ break;
+ }
case WebAssembly::CALL_INDIRECT_S:
case WebAssembly::RET_CALL_INDIRECT_S: {
// A special case for call_indirect (and ret_call_indirect), if the table
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
index ca9a5ef9dda1c..485b12e256224 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
@@ -64,7 +64,7 @@ defm CALL_INDIRECT :
(outs),
(ins TypeIndex:$type, table32_op:$table),
[],
- "call_indirect", "call_indirect\t$type, $table", 0x11>;
+ "call_indirect\t$type, $table", "call_indirect\t$type, $table", 0x11>;
let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in
defm RET_CALL :
@@ -77,7 +77,7 @@ let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in
defm RET_CALL_INDIRECT :
I<(outs), (ins TypeIndex:$type, table32_op:$table, variable_ops),
(outs), (ins TypeIndex:$type, table32_op:$table), [],
- "return_call_indirect\t", "return_call_indirect\t$type, $table",
+ "return_call_indirect\t$type, $table", "return_call_indirect\t$type, $table",
0x13>,
Requires<[HasTailCall]>;
diff --git a/llvm/test/CodeGen/WebAssembly/call-pic.ll b/llvm/test/CodeGen/WebAssembly/call-pic.ll
index f6f24bdd0530c..53943246a8f92 100644
--- a/llvm/test/CodeGen/WebAssembly/call-pic.ll
+++ b/llvm/test/CodeGen/WebAssembly/call-pic.ll
@@ -19,7 +19,7 @@ define void @call_indirect_func() {
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, indirect_func at MBREL{{$}}
; CHECK-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
; CHECK-NEXT: i32.load $push[[L3:[0-9]+]]=, 0($pop[[L2]]){{$}}
-; CHECK-NEXT: call_indirect $push[[L4:[0-9]+]]=, $pop[[L3]]{{$}}
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (i32), $push[[L4:[0-9]+]]=, $pop[[L3]]{{$}}
%1 = load ptr, ptr @indirect_func, align 4
%call = call i32 %1()
ret void
diff --git a/llvm/test/CodeGen/WebAssembly/call.ll b/llvm/test/CodeGen/WebAssembly/call.ll
index 2f7658ac5a6cc..2a1ad944ba6e0 100644
--- a/llvm/test/CodeGen/WebAssembly/call.ll
+++ b/llvm/test/CodeGen/WebAssembly/call.ll
@@ -94,7 +94,7 @@ define i32 @call_i32_binary(i32 %a, i32 %b) {
; CHECK-LABEL: call_indirect_void:
; CHECK-NEXT: .functype call_indirect_void (i32) -> (){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $pop[[L0]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, () -> (), $pop[[L0]]{{$}}
; CHECK-NEXT: return{{$}}
define void @call_indirect_void(ptr %callee) {
call void %callee()
@@ -104,7 +104,7 @@ define void @call_indirect_void(ptr %callee) {
; CHECK-LABEL: call_indirect_i32:
; CHECK-NEXT: .functype call_indirect_i32 (i32) -> (i32){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (i32), $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
; CHECK-NEXT: return $pop[[NUM]]{{$}}
define i32 @call_indirect_i32(ptr %callee) {
%t = call i32 %callee()
@@ -114,7 +114,7 @@ define i32 @call_indirect_i32(ptr %callee) {
; CHECK-LABEL: call_indirect_i64:
; CHECK-NEXT: .functype call_indirect_i64 (i32) -> (i64){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, () -> (i64), $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
; CHECK-NEXT: return $pop[[NUM]]{{$}}
define i64 @call_indirect_i64(ptr %callee) {
%t = call i64 %callee()
@@ -124,7 +124,7 @@ define i64 @call_indirect_i64(ptr %callee) {
; CHECK-LABEL: call_indirect_float:
; CHECK-NEXT: .functype call_indirect_float (i32) -> (f32){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, () -> (f32), $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
; CHECK-NEXT: return $pop[[NUM]]{{$}}
define float @call_indirect_float(ptr %callee) {
%t = call float %callee()
@@ -134,7 +134,7 @@ define float @call_indirect_float(ptr %callee) {
; CHECK-LABEL: call_indirect_double:
; CHECK-NEXT: .functype call_indirect_double (i32) -> (f64){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, () -> (f64), $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
; CHECK-NEXT: return $pop[[NUM]]{{$}}
define double @call_indirect_double(ptr %callee) {
%t = call double %callee()
@@ -144,7 +144,7 @@ define double @call_indirect_double(ptr %callee) {
; CHECK-LABEL: call_indirect_v128:
; CHECK-NEXT: .functype call_indirect_v128 (i32) -> (v128){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, () -> (v128), $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}}
; CHECK-NEXT: return $pop[[NUM]]{{$}}
define <16 x i8> @call_indirect_v128(ptr %callee) {
%t = call <16 x i8> %callee()
@@ -155,7 +155,7 @@ define <16 x i8> @call_indirect_v128(ptr %callee) {
; CHECK-NEXT: .functype call_indirect_arg (i32, i32) -> (){{$}}
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 1{{$}}
; CHECK-NEXT: local.get $push[[L1:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, (i32) -> (), $pop[[L0]], $pop[[L1]]{{$}}
; CHECK-NEXT: return{{$}}
define void @call_indirect_arg(ptr %callee, i32 %arg) {
call void %callee(i32 %arg)
@@ -167,7 +167,7 @@ define void @call_indirect_arg(ptr %callee, i32 %arg) {
; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 1{{$}}
; CHECK-NEXT: local.get $push[[L1:[0-9]+]]=, 2{{$}}
; CHECK-NEXT: local.get $push[[L2:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]], $pop[[L2]]{{$}}
+; CHECK-NEXT: {{^}} call_indirect __indirect_function_table, (i32, i32) -> (i32), $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]], $pop[[L2]]{{$}}
; CHECK-NEXT: drop $pop[[NUM]]{{$}}
; CHECK-NEXT: return{{$}}
define void @call_indirect_arg_2(ptr %callee, i32 %arg, i32 %arg2) {
@@ -219,7 +219,7 @@ define void @coldcc_tail_call_void_nullary() {
; CHECK-NEXT: i32.const $push[[L3:[0-9]+]]=, void_nullary{{$}}
; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, other_void_nullary{{$}}
; CHECK-NEXT: i32.add $push[[L4:[0-9]+]]=, $pop[[L3]], $pop[[L2]]{{$}}
-; CHECK-NEXT: call_indirect $pop[[L4]]{{$}}
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L4]]{{$}}
; CHECK-NEXT: call void_nullary{{$}}
; CHECK-NEXT: return{{$}}
declare void @vararg_func(...)
@@ -243,7 +243,7 @@ bb2:
; CHECK-NEXT: local.get $push{{.*}}=, [[L0]]
; CHECK-NEXT: i32.const $push{{.*}}=, 12
; CHECK-NEXT: i32.add
-; CHECK-NEXT: call_indirect $pop{{.*}}
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop{{.*}}
define void @call_indirect_alloca() {
entry:
%ptr = alloca i32, align 4
@@ -254,9 +254,9 @@ entry:
; Calling non-functional globals should be lowered to call_indirects.
; CHECK-LABEL: call_indirect_int:
; CHECK: i32.const $push[[L0:[0-9]+]]=, global_i8
-; CHECK-NEXT: call_indirect $pop[[L0]]
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L0]]
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, global_i32
-; CHECK-NEXT: call_indirect $pop[[L1]]
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L1]]
@global_i8 = global i8 0
@global_i32 = global i32 0
define void @call_indirect_int() {
@@ -268,9 +268,9 @@ define void @call_indirect_int() {
; Calling aliases of non-functional globals should be lowered to call_indirects.
; CHECK-LABEL: call_indirect_int_alias:
; CHECK: i32.const $push[[L0:[0-9]+]]=, global_i8_alias
-; CHECK-NEXT: call_indirect $pop[[L0]]
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L0]]
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, global_i32_alias
-; CHECK-NEXT: call_indirect $pop[[L1]]
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L1]]
@global_i8_alias = alias i8, ptr @global_i8
@global_i32_alias = alias i32, ptr @global_i32
define void @call_indirect_int_alias() {
@@ -284,7 +284,7 @@ define void @call_indirect_int_alias() {
; CHECK-LABEL: call_func_alias:
; SLOW: call func_alias
; FAST: i32.const $push[[L0:[0-9]+]]=, func_alias
-; FAST-NEXT: call_indirect $pop[[L0]]
+; FAST-NEXT: call_indirect __indirect_function_table, () -> (), $pop[[L0]]
@func_alias = alias void (), ptr @call_void_nullary
define void @call_func_alias() {
call void @func_alias()
diff --git a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
index 0553f61ef9056..c623e7287c034 100644
--- a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
+++ b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
@@ -121,7 +121,7 @@ define void @test_store() {
; CHECK-NEXT: .functype test_load () -> (i32){{$}}
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0{{$}}
; CHECK-NEXT: i32.load $push[[L1:[0-9]+]]=, global_func($pop[[L0]]){{$}}
-; CHECK-NEXT: call_indirect $push{{[0-9]+}}=, $pop[[L1]]{{$}}
+; CHECK-NEXT: call_indirect __indirect_function_table, () -> (i32), $push{{[0-9]+}}=, $pop[[L1]]{{$}}
define i32 @test_load() {
%1 = load ptr, ptr @global_func
%2 = call i32 %1()
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll
index 5001db7e57a1e..2fa6381e4c4c7 100644
--- a/llvm/test/CodeGen/WebAssembly/multivalue.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll
@@ -62,7 +62,7 @@ define %pair @pair_call_return() {
; CHECK-NEXT: call_indirect () -> (i32, i64){{$}}
; REF: call_indirect __indirect_function_table, () -> (i32, i64){{$}}
; CHECK-NEXT: end_function{{$}}
-; REGS: call_indirect $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
+; REGS: call_indirect 0, () -> (i32, i64), $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
define %pair @pair_call_indirect(ptr %f) {
%p = call %pair %f()
ret %pair %p
diff --git a/llvm/test/CodeGen/WebAssembly/reg-stackify.ll b/llvm/test/CodeGen/WebAssembly/reg-stackify.ll
index f1466a48e7394..f9c233d1be3a3 100644
--- a/llvm/test/CodeGen/WebAssembly/reg-stackify.ll
+++ b/llvm/test/CodeGen/WebAssembly/reg-stackify.ll
@@ -643,7 +643,7 @@ define i32 @stackpointer_dependency(ptr readnone) {
; CHECK-NEXT: local.tee $push[[L3:.+]]=, $0=, $pop[[L4]]
; CHECK-NEXT: i32.load $push[[L0:.+]]=, 0($0)
; CHECK-NEXT: i32.load $push[[L1:.+]]=, 0($pop[[L0]])
-; CHECK-NEXT: call_indirect $push{{.+}}=, $pop[[L3]], $1, $pop[[L1]]
+; CHECK-NEXT: call_indirect __indirect_function_table, (i32, i32) -> (i32), $push{{.+}}=, $pop[[L3]], $1, $pop[[L1]]
; NOREGS-LABEL: call_indirect_stackify:
; NOREGS: i32.load 0
; NOREGS-NEXT: local.tee 0
diff --git a/llvm/test/CodeGen/WebAssembly/swiftcc.ll b/llvm/test/CodeGen/WebAssembly/swiftcc.ll
index 0c5c3d8bbb0ff..b6cc6fb441ee0 100644
--- a/llvm/test/CodeGen/WebAssembly/swiftcc.ll
+++ b/llvm/test/CodeGen/WebAssembly/swiftcc.ll
@@ -18,21 +18,21 @@ define swiftcc void @bar() {
; REG: call foo, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
call swiftcc void @foo(i32 1, i32 2)
-; REG: call_indirect $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
+; REG: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> (), $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
; CHECK: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> ()
call swiftcc void %1(i32 1, i32 2)
-; REG: call_indirect $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
+; REG: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> (), $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
; CHECK: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> ()
call swiftcc void %1(i32 1, i32 2, i32 swiftself 3)
%err = alloca swifterror ptr, align 4
-; REG: call_indirect $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
+; REG: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> (), $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
; CHECK: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> ()
call swiftcc void %1(i32 1, i32 2, ptr swifterror %err)
-; REG: call_indirect $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
+; REG: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> (), $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}
; CHECK: call_indirect __indirect_function_table, (i32, i32, i32, i32) -> ()
call swiftcc void %1(i32 1, i32 2, i32 swiftself 3, ptr swifterror %err)
diff --git a/llvm/test/CodeGen/WebAssembly/tailcall.ll b/llvm/test/CodeGen/WebAssembly/tailcall.ll
index 84bd142462e37..e05b65f51a073 100644
--- a/llvm/test/CodeGen/WebAssembly/tailcall.ll
+++ b/llvm/test/CodeGen/WebAssembly/tailcall.ll
@@ -82,7 +82,7 @@ define i32 @indirect_notail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_notail:
; CHECK: .functype indirect_notail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: return $pop0
%p = extractvalue %fn %f, 0
%v = notail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -93,7 +93,7 @@ define i32 @indirect_musttail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_musttail:
; CHECK: .functype indirect_musttail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect , $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect 0, (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = musttail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -103,7 +103,7 @@ define i32 @indirect_tail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_tail:
; CHECK: .functype indirect_tail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect , $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect 0, (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -118,7 +118,7 @@ define i1 @choice_notail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: call_indirect $push5=, $0, $pop4 # Invalid depth argument!
+; SLOW-NEXT: call_indirect 0, (i32) -> (i32), $push5=, $0, $pop4 # Invalid depth argument!
; SLOW-NEXT: return $pop5
;
; FAST-LABEL: choice_notail:
@@ -129,7 +129,7 @@ define i1 @choice_notail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect 0, (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = notail call i1 %p(i1 %x)
@@ -145,7 +145,7 @@ define i1 @choice_musttail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect , $0, $pop4
+; SLOW-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_musttail:
; FAST: .functype choice_musttail (i32) -> (i32)
@@ -155,7 +155,7 @@ define i1 @choice_musttail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push0=, $pop4, $pop3, $pop2
-; FAST-NEXT: return_call_indirect , $0, $pop0
+; FAST-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = musttail call i1 %p(i1 %x)
ret i1 %v
@@ -170,7 +170,7 @@ define i1 @choice_tail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect , $0, $pop4
+; SLOW-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_tail:
; FAST: .functype choice_tail (i32) -> (i32)
@@ -180,7 +180,7 @@ define i1 @choice_tail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect 0, (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = tail call i1 %p(i1 %x)
@@ -264,7 +264,7 @@ define void @mismatched_indirect_void(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_void:
; CHECK: .functype mismatched_indirect_void (i32, i32, i32) -> ()
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect $drop=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $drop=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: return
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -275,7 +275,7 @@ define float @mismatched_indirect_f32(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_f32:
; CHECK: .functype mismatched_indirect_f32 (i32, i32, i32) -> (f32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: f32.reinterpret_i32 $push1=, $pop0
; CHECK-NEXT: return $pop1
%p = extractvalue %fn %f, 0
@@ -538,7 +538,7 @@ define i32 @indirect_epilogue(ptr %p) {
; CHECK-NEXT: i32.const $push2=, 256
; CHECK-NEXT: i32.add $push3=, $1, $pop2
; CHECK-NEXT: global.set __stack_pointer, $pop3
-; CHECK-NEXT: return_call_indirect , $0, $0
+; CHECK-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $0
%a = alloca [64 x i32]
%v = musttail call i32 %p(ptr %p)
ret i32 %v
@@ -564,7 +564,7 @@ define i32 @unique_caller(ptr %p) {
; SLOW-NEXT: i64.const $push2=, 0
; SLOW-NEXT: f64.const $push1=, 0x0p0
; SLOW-NEXT: i32.load $push0=, 0($0)
-; SLOW-NEXT: return_call_indirect , $pop4, $pop3, $pop2, $pop1, $pop0
+; SLOW-NEXT: return_call_indirect 0, (i32, f32, i64, f64) -> (i32), $pop4, $pop3, $pop2, $pop1, $pop0
;
; FAST-LABEL: unique_caller:
; FAST: .functype unique_caller (i32) -> (i32)
@@ -576,7 +576,7 @@ define i32 @unique_caller(ptr %p) {
; FAST-NEXT: i32.const $push6=, 0
; FAST-NEXT: f64.convert_i32_s $push4=, $pop6
; FAST-NEXT: i32.load $push5=, 0($0)
-; FAST-NEXT: call_indirect $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect 0, (i32, f32, i64, f64) -> (i32), $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%f = load ptr, ptr %p
%v = tail call i32 %f(i32 0, float 0., i64 0, double 0.)
>From 89caa96f44330967025b24b944722bee327b2822 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Mon, 9 Feb 2026 00:43:33 +0900
Subject: [PATCH 02/12] consolidate handling indirect for register and stack
---
.../MCTargetDesc/WebAssemblyInstPrinter.cpp | 34 ++++++-------------
llvm/test/CodeGen/WebAssembly/multivalue.ll | 2 +-
llvm/test/CodeGen/WebAssembly/tailcall.ll | 28 +++++++--------
3 files changed, 26 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index f883c5175a408..c59291b270ab9 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -48,25 +48,17 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot,
const MCSubtargetInfo &STI,
raw_ostream &OS) {
+
+ unsigned TypeOperand = 0;
+ unsigned TableOperand = 1;
switch (MI->getOpcode()) {
- case WebAssembly::CALL_INDIRECT:
- case WebAssembly::RET_CALL_INDIRECT: {
- OS << "\t" << getMnemonic(*MI).first << "\t";
- unsigned TypeOperand = 0;
- unsigned TableOperand = 1;
- if (MI->getOpcode() == WebAssembly::CALL_INDIRECT) {
- unsigned NumDefs = MI->getOperand(0).getImm();
- TypeOperand = 1 + NumDefs; // Type 위치 보정
- TableOperand = 1 + NumDefs + 1; // Table 위치 보정
- }
- printOperand(MI, TableOperand, OS);
- OS << ", ";
- printOperand(MI, TypeOperand, OS);
- if (MI->getOpcode() == WebAssembly::CALL_INDIRECT) {
- OS << ", ";
- }
- break;
+ case WebAssembly::CALL_INDIRECT: {
+ unsigned NumDefs = MI->getOperand(0).getImm();
+ TypeOperand = NumDefs + 1;
+ TableOperand = NumDefs + 2;
+ [[fallthrough]];
}
+ case WebAssembly::RET_CALL_INDIRECT:
case WebAssembly::CALL_INDIRECT_S:
case WebAssembly::RET_CALL_INDIRECT_S: {
// A special case for call_indirect (and ret_call_indirect), if the table
@@ -77,17 +69,13 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
OS << "\t";
OS << getMnemonic(*MI).first;
OS << " ";
-
- assert(MI->getNumOperands() == 2);
- const unsigned TypeOperand = 0;
- const unsigned TableOperand = 1;
if (MI->getOperand(TableOperand).isExpr()) {
printOperand(MI, TableOperand, OS);
OS << ", ";
- } else {
- assert(MI->getOperand(TableOperand).getImm() == 0);
}
printOperand(MI, TypeOperand, OS);
+ if (MI->getOpcode() == WebAssembly::CALL_INDIRECT)
+ OS << ", ";
break;
}
default:
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll
index 2fa6381e4c4c7..537adff3f3405 100644
--- a/llvm/test/CodeGen/WebAssembly/multivalue.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll
@@ -62,7 +62,7 @@ define %pair @pair_call_return() {
; CHECK-NEXT: call_indirect () -> (i32, i64){{$}}
; REF: call_indirect __indirect_function_table, () -> (i32, i64){{$}}
; CHECK-NEXT: end_function{{$}}
-; REGS: call_indirect 0, () -> (i32, i64), $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
+; REGS: call_indirect () -> (i32, i64), $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
define %pair @pair_call_indirect(ptr %f) {
%p = call %pair %f()
ret %pair %p
diff --git a/llvm/test/CodeGen/WebAssembly/tailcall.ll b/llvm/test/CodeGen/WebAssembly/tailcall.ll
index e05b65f51a073..8900fe9cff24b 100644
--- a/llvm/test/CodeGen/WebAssembly/tailcall.ll
+++ b/llvm/test/CodeGen/WebAssembly/tailcall.ll
@@ -82,7 +82,7 @@ define i32 @indirect_notail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_notail:
; CHECK: .functype indirect_notail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: return $pop0
%p = extractvalue %fn %f, 0
%v = notail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -93,7 +93,7 @@ define i32 @indirect_musttail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_musttail:
; CHECK: .functype indirect_musttail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect 0, (i32, i32, i32) -> (i32), $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = musttail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -103,7 +103,7 @@ define i32 @indirect_tail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_tail:
; CHECK: .functype indirect_tail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect 0, (i32, i32, i32) -> (i32), $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -118,7 +118,7 @@ define i1 @choice_notail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: call_indirect 0, (i32) -> (i32), $push5=, $0, $pop4 # Invalid depth argument!
+; SLOW-NEXT: call_indirect (i32) -> (i32), $push5=, $0, $pop4 # Invalid depth argument!
; SLOW-NEXT: return $pop5
;
; FAST-LABEL: choice_notail:
@@ -129,7 +129,7 @@ define i1 @choice_notail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect 0, (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = notail call i1 %p(i1 %x)
@@ -145,7 +145,7 @@ define i1 @choice_musttail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop4
+; SLOW-NEXT: return_call_indirect (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_musttail:
; FAST: .functype choice_musttail (i32) -> (i32)
@@ -155,7 +155,7 @@ define i1 @choice_musttail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push0=, $pop4, $pop3, $pop2
-; FAST-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop0
+; FAST-NEXT: return_call_indirect (i32) -> (i32), $0, $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = musttail call i1 %p(i1 %x)
ret i1 %v
@@ -170,7 +170,7 @@ define i1 @choice_tail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $pop4
+; SLOW-NEXT: return_call_indirect (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_tail:
; FAST: .functype choice_tail (i32) -> (i32)
@@ -180,7 +180,7 @@ define i1 @choice_tail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect 0, (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = tail call i1 %p(i1 %x)
@@ -264,7 +264,7 @@ define void @mismatched_indirect_void(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_void:
; CHECK: .functype mismatched_indirect_void (i32, i32, i32) -> ()
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $drop=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $drop=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: return
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -275,7 +275,7 @@ define float @mismatched_indirect_f32(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_f32:
; CHECK: .functype mismatched_indirect_f32 (i32, i32, i32) -> (f32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect 0, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
; CHECK-NEXT: f32.reinterpret_i32 $push1=, $pop0
; CHECK-NEXT: return $pop1
%p = extractvalue %fn %f, 0
@@ -538,7 +538,7 @@ define i32 @indirect_epilogue(ptr %p) {
; CHECK-NEXT: i32.const $push2=, 256
; CHECK-NEXT: i32.add $push3=, $1, $pop2
; CHECK-NEXT: global.set __stack_pointer, $pop3
-; CHECK-NEXT: return_call_indirect 0, (i32) -> (i32), $0, $0
+; CHECK-NEXT: return_call_indirect (i32) -> (i32), $0, $0
%a = alloca [64 x i32]
%v = musttail call i32 %p(ptr %p)
ret i32 %v
@@ -564,7 +564,7 @@ define i32 @unique_caller(ptr %p) {
; SLOW-NEXT: i64.const $push2=, 0
; SLOW-NEXT: f64.const $push1=, 0x0p0
; SLOW-NEXT: i32.load $push0=, 0($0)
-; SLOW-NEXT: return_call_indirect 0, (i32, f32, i64, f64) -> (i32), $pop4, $pop3, $pop2, $pop1, $pop0
+; SLOW-NEXT: return_call_indirect (i32, f32, i64, f64) -> (i32), $pop4, $pop3, $pop2, $pop1, $pop0
;
; FAST-LABEL: unique_caller:
; FAST: .functype unique_caller (i32) -> (i32)
@@ -576,7 +576,7 @@ define i32 @unique_caller(ptr %p) {
; FAST-NEXT: i32.const $push6=, 0
; FAST-NEXT: f64.convert_i32_s $push4=, $pop6
; FAST-NEXT: i32.load $push5=, 0($0)
-; FAST-NEXT: call_indirect 0, (i32, f32, i64, f64) -> (i32), $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect (i32, f32, i64, f64) -> (i32), $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument!
; FAST-NEXT: return $pop0
%f = load ptr, ptr %p
%v = tail call i32 %f(i32 0, float 0., i64 0, double 0.)
>From 3ee0a2faea96dd9d019c5942edda6db0d420e3b8 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Thu, 12 Feb 2026 18:27:10 +0900
Subject: [PATCH 03/12] add test return-call-indirect
---
.../WebAssembly/return-call-indirect.ll | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
diff --git a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
new file mode 100644
index 0000000000000..e8375aaedd752
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
@@ -0,0 +1,29 @@
+; RUN: llc < %s -asm-verbose=false -mattr=-reference-types,+tail-call -O2 | FileCheck --check-prefixes=CHECK,NOREF %s
+; RUN: llc < %s -asm-verbose=false -mattr=+reference-types,+tail-call -O2 | FileCheck --check-prefixes=CHECK,REF %s
+; RUN: llc < %s -asm-verbose=false -mattr=+tail-call -O2 --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s
+
+; Test that compilation units with call_indirect but without any
+; function pointer declarations still get a table.
+
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: return_call_indirect:
+; CHECK-NEXT: .functype return_call_indirect (i32) -> (i32)
+; CHECK-NEXT: local.get 0
+; REF: return_call_indirect __indirect_function_table, () -> (i32)
+; NOREF: return_call_indirect __indirect_function_table, () -> (i32)
+; CHECK-NEXT: end_function
+define i32 @return_call_indirect(ptr %callee) {
+ %r = tail call i32 %callee()
+ ret i32 %r
+}
+
+; OBJ: Imports:
+; OBJ-NEXT: - Module: env
+; OBJ-NEXT: Field: __linear_memory
+; OBJ-NEXT: Kind: MEMORY
+; OBJ-NEXT: Memory:
+; OBJ-NEXT: Minimum: 0x0
+; OBJ-NEXT: - Module: env
+; OBJ-NEXT: Field: __indirect_function_table
+; OBJ-NEXT: Kind: TABLE
>From 152693d307d659a9c32441f51e3f21f0ea612abc Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 01:43:55 +0900
Subject: [PATCH 04/12] remove blank line
---
.../Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index c59291b270ab9..660dab93d18c9 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -48,7 +48,6 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot,
const MCSubtargetInfo &STI,
raw_ostream &OS) {
-
unsigned TypeOperand = 0;
unsigned TableOperand = 1;
switch (MI->getOpcode()) {
>From 06bdd8658540f99735f6961040999446c75d1707 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 01:48:41 +0900
Subject: [PATCH 05/12] Fix conflict between printer and parser
When processing the `call_indirect` and `return_call_indirect`
instructions, WebAssemblyAsmParser only parsed the symbol table
when the `+reference-type` feature was present. However,
WebAssemblyInstPrinter always printed the symbol table, creating
a discrepancy. To resolve this, WebAssemblyInstPrinter has been
modified to print the symbol table only when the `+reference-type`
feature is present.
---
.../Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp | 3 ++-
llvm/test/CodeGen/WebAssembly/return-call-indirect.ll | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 660dab93d18c9..963a2783aeba9 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -68,7 +68,8 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
OS << "\t";
OS << getMnemonic(*MI).first;
OS << " ";
- if (MI->getOperand(TableOperand).isExpr()) {
+ if (MI->getOperand(TableOperand).isExpr() &&
+ STI.checkFeatures("+reference-types")) {
printOperand(MI, TableOperand, OS);
OS << ", ";
}
diff --git a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
index e8375aaedd752..0b4f4882ebcf2 100644
--- a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
+++ b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
@@ -11,7 +11,7 @@ target triple = "wasm32-unknown-unknown"
; CHECK-NEXT: .functype return_call_indirect (i32) -> (i32)
; CHECK-NEXT: local.get 0
; REF: return_call_indirect __indirect_function_table, () -> (i32)
-; NOREF: return_call_indirect __indirect_function_table, () -> (i32)
+; NOREF: return_call_indirect () -> (i32)
; CHECK-NEXT: end_function
define i32 @return_call_indirect(ptr %callee) {
%r = tail call i32 %callee()
>From 8ce1f2d3afe988a37af1c62936acb3db5c700eb6 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 12:59:30 +0900
Subject: [PATCH 06/12] update testcases of tailcall.ll
---
llvm/test/CodeGen/WebAssembly/tailcall.ll | 43 +++++++++++------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/llvm/test/CodeGen/WebAssembly/tailcall.ll b/llvm/test/CodeGen/WebAssembly/tailcall.ll
index 8900fe9cff24b..7f6a3ebc2351d 100644
--- a/llvm/test/CodeGen/WebAssembly/tailcall.ll
+++ b/llvm/test/CodeGen/WebAssembly/tailcall.ll
@@ -1,12 +1,10 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
-; RUN: llc < %s -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,SLOW %s
-; RUN: llc < %s -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -fast-isel -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,FAST %s
-; RUN: llc < %s --filetype=obj -mattr=+tail-call | obj2yaml | FileCheck --check-prefix=YAML %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+tail-call | FileCheck --check-prefixes=CHECK,SLOW %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -fast-isel -mattr=+tail-call | FileCheck --check-prefixes=CHECK,FAST %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown --filetype=obj -mattr=+tail-call | obj2yaml | FileCheck --check-prefix=YAML %s
; Test that the tail calls lower correctly
-target triple = "wasm32-unknown-unknown"
-
%fn = type <{ptr}>
declare i1 @foo(i1)
declare i1 @bar(i1)
@@ -82,7 +80,7 @@ define i32 @indirect_notail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_notail:
; CHECK: .functype indirect_notail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect __indirect_function_table, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0
; CHECK-NEXT: return $pop0
%p = extractvalue %fn %f, 0
%v = notail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -93,7 +91,7 @@ define i32 @indirect_musttail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_musttail:
; CHECK: .functype indirect_musttail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect (i32, i32, i32) -> (i32), $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect __indirect_function_table, (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = musttail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -103,7 +101,7 @@ define i32 @indirect_tail(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: indirect_tail:
; CHECK: .functype indirect_tail (i32, i32, i32) -> (i32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: return_call_indirect (i32, i32, i32) -> (i32), $0, $1, $2, $0
+; CHECK-NEXT: return_call_indirect __indirect_function_table, (i32, i32, i32) -> (i32), $0, $1, $2, $0
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
@@ -118,7 +116,7 @@ define i1 @choice_notail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: call_indirect (i32) -> (i32), $push5=, $0, $pop4 # Invalid depth argument!
+; SLOW-NEXT: call_indirect __indirect_function_table, (i32) -> (i32), $push5=, $0, $pop4
; SLOW-NEXT: return $pop5
;
; FAST-LABEL: choice_notail:
@@ -129,7 +127,7 @@ define i1 @choice_notail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect __indirect_function_table, (i32) -> (i32), $push0=, $0, $pop5
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = notail call i1 %p(i1 %x)
@@ -145,7 +143,7 @@ define i1 @choice_musttail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect (i32) -> (i32), $0, $pop4
+; SLOW-NEXT: return_call_indirect __indirect_function_table, (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_musttail:
; FAST: .functype choice_musttail (i32) -> (i32)
@@ -155,7 +153,7 @@ define i1 @choice_musttail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push0=, $pop4, $pop3, $pop2
-; FAST-NEXT: return_call_indirect (i32) -> (i32), $0, $pop0
+; FAST-NEXT: return_call_indirect __indirect_function_table, (i32) -> (i32), $0, $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = musttail call i1 %p(i1 %x)
ret i1 %v
@@ -170,7 +168,7 @@ define i1 @choice_tail(i1 %x) {
; SLOW-NEXT: i32.const $push0=, 1
; SLOW-NEXT: i32.and $push1=, $0, $pop0
; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1
-; SLOW-NEXT: return_call_indirect (i32) -> (i32), $0, $pop4
+; SLOW-NEXT: return_call_indirect __indirect_function_table, (i32) -> (i32), $0, $pop4
;
; FAST-LABEL: choice_tail:
; FAST: .functype choice_tail (i32) -> (i32)
@@ -180,7 +178,7 @@ define i1 @choice_tail(i1 %x) {
; FAST-NEXT: i32.const $push1=, 1
; FAST-NEXT: i32.and $push2=, $0, $pop1
; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2
-; FAST-NEXT: call_indirect (i32) -> (i32), $push0=, $0, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect __indirect_function_table, (i32) -> (i32), $push0=, $0, $pop5
; FAST-NEXT: return $pop0
%p = select i1 %x, ptr @foo, ptr @bar
%v = tail call i1 %p(i1 %x)
@@ -264,7 +262,7 @@ define void @mismatched_indirect_void(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_void:
; CHECK: .functype mismatched_indirect_void (i32, i32, i32) -> ()
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $drop=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect __indirect_function_table, (i32, i32, i32) -> (i32), $drop=, $0, $1, $2, $0
; CHECK-NEXT: return
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
@@ -275,7 +273,7 @@ define float @mismatched_indirect_f32(%fn %f, i32 %x, i32 %y) {
; CHECK-LABEL: mismatched_indirect_f32:
; CHECK: .functype mismatched_indirect_f32 (i32, i32, i32) -> (f32)
; CHECK-NEXT: # %bb.0:
-; CHECK-NEXT: call_indirect (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0 # Invalid depth argument!
+; CHECK-NEXT: call_indirect __indirect_function_table, (i32, i32, i32) -> (i32), $push0=, $0, $1, $2, $0
; CHECK-NEXT: f32.reinterpret_i32 $push1=, $pop0
; CHECK-NEXT: return $pop1
%p = extractvalue %fn %f, 0
@@ -538,7 +536,7 @@ define i32 @indirect_epilogue(ptr %p) {
; CHECK-NEXT: i32.const $push2=, 256
; CHECK-NEXT: i32.add $push3=, $1, $pop2
; CHECK-NEXT: global.set __stack_pointer, $pop3
-; CHECK-NEXT: return_call_indirect (i32) -> (i32), $0, $0
+; CHECK-NEXT: return_call_indirect __indirect_function_table, (i32) -> (i32), $0, $0
%a = alloca [64 x i32]
%v = musttail call i32 %p(ptr %p)
ret i32 %v
@@ -564,7 +562,7 @@ define i32 @unique_caller(ptr %p) {
; SLOW-NEXT: i64.const $push2=, 0
; SLOW-NEXT: f64.const $push1=, 0x0p0
; SLOW-NEXT: i32.load $push0=, 0($0)
-; SLOW-NEXT: return_call_indirect (i32, f32, i64, f64) -> (i32), $pop4, $pop3, $pop2, $pop1, $pop0
+; SLOW-NEXT: return_call_indirect __indirect_function_table, (i32, f32, i64, f64) -> (i32), $pop4, $pop3, $pop2, $pop1, $pop0
;
; FAST-LABEL: unique_caller:
; FAST: .functype unique_caller (i32) -> (i32)
@@ -576,7 +574,7 @@ define i32 @unique_caller(ptr %p) {
; FAST-NEXT: i32.const $push6=, 0
; FAST-NEXT: f64.convert_i32_s $push4=, $pop6
; FAST-NEXT: i32.load $push5=, 0($0)
-; FAST-NEXT: call_indirect (i32, f32, i64, f64) -> (i32), $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument!
+; FAST-NEXT: call_indirect __indirect_function_table, (i32, f32, i64, f64) -> (i32), $push0=, $pop1, $pop2, $pop3, $pop4, $pop5
; FAST-NEXT: return $pop0
%f = load ptr, ptr %p
%v = tail call i32 %f(i32 0, float 0., i64 0, double 0.)
@@ -584,7 +582,6 @@ define i32 @unique_caller(ptr %p) {
}
; CHECK-LABEL: .section .custom_section.target_features
-; CHECK-NEXT: .int8 1
-; CHECK-NEXT: .int8 43
-; CHECK-NEXT: .int8 9
-; CHECK-NEXT: .ascii "tail-call"
+; CHECK: .ascii "reference-types"
+; CHECK: .ascii "tail-call"
+; CHECK-LABEL: .section .text.unique_caller
>From bb6e312ded9280706b07fca2d3c3dcc28ff582da Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 13:00:19 +0900
Subject: [PATCH 07/12] fix mistypo of return-call-indirect.ll
---
llvm/test/CodeGen/WebAssembly/return-call-indirect.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
index 0b4f4882ebcf2..7121aece14d08 100644
--- a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
+++ b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
@@ -2,7 +2,7 @@
; RUN: llc < %s -asm-verbose=false -mattr=+reference-types,+tail-call -O2 | FileCheck --check-prefixes=CHECK,REF %s
; RUN: llc < %s -asm-verbose=false -mattr=+tail-call -O2 --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s
-; Test that compilation units with call_indirect but without any
+; Test that compilation units with return_call_indirect but without any
; function pointer declarations still get a table.
target triple = "wasm32-unknown-unknown"
>From 821e13d5193587079669be2016c804dc2ea634db Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 13:04:50 +0900
Subject: [PATCH 08/12] fix recover missed assert
---
.../Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 963a2783aeba9..7c09d7910c56d 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -72,6 +72,8 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
STI.checkFeatures("+reference-types")) {
printOperand(MI, TableOperand, OS);
OS << ", ";
+ } else {
+ assert(MI->getOperand(TableOperand).getImm() == 0);
}
printOperand(MI, TypeOperand, OS);
if (MI->getOpcode() == WebAssembly::CALL_INDIRECT)
>From 1aaf9cc12e517cd09a6ce7eda617e15badc24853 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 13 Feb 2026 13:27:58 +0900
Subject: [PATCH 09/12] fix wrong condition
---
.../WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 7c09d7910c56d..37b58daac644c 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -68,10 +68,11 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
OS << "\t";
OS << getMnemonic(*MI).first;
OS << " ";
- if (MI->getOperand(TableOperand).isExpr() &&
- STI.checkFeatures("+reference-types")) {
- printOperand(MI, TableOperand, OS);
- OS << ", ";
+ if (MI->getOperand(TableOperand).isExpr()) {
+ if (STI.checkFeatures("+reference-types")) {
+ printOperand(MI, TableOperand, OS);
+ OS << ", ";
+ }
} else {
assert(MI->getOperand(TableOperand).getImm() == 0);
}
>From 41ed5b6a273c52e0f447545c5c519c7c1448bf70 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sat, 14 Feb 2026 15:39:52 +0900
Subject: [PATCH 10/12] Removing unnecessary test files
---
.../WebAssembly/return-call-indirect.ll | 29 -------------------
1 file changed, 29 deletions(-)
delete mode 100644 llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
diff --git a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll b/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
deleted file mode 100644
index 7121aece14d08..0000000000000
--- a/llvm/test/CodeGen/WebAssembly/return-call-indirect.ll
+++ /dev/null
@@ -1,29 +0,0 @@
-; RUN: llc < %s -asm-verbose=false -mattr=-reference-types,+tail-call -O2 | FileCheck --check-prefixes=CHECK,NOREF %s
-; RUN: llc < %s -asm-verbose=false -mattr=+reference-types,+tail-call -O2 | FileCheck --check-prefixes=CHECK,REF %s
-; RUN: llc < %s -asm-verbose=false -mattr=+tail-call -O2 --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s
-
-; Test that compilation units with return_call_indirect but without any
-; function pointer declarations still get a table.
-
-target triple = "wasm32-unknown-unknown"
-
-; CHECK-LABEL: return_call_indirect:
-; CHECK-NEXT: .functype return_call_indirect (i32) -> (i32)
-; CHECK-NEXT: local.get 0
-; REF: return_call_indirect __indirect_function_table, () -> (i32)
-; NOREF: return_call_indirect () -> (i32)
-; CHECK-NEXT: end_function
-define i32 @return_call_indirect(ptr %callee) {
- %r = tail call i32 %callee()
- ret i32 %r
-}
-
-; OBJ: Imports:
-; OBJ-NEXT: - Module: env
-; OBJ-NEXT: Field: __linear_memory
-; OBJ-NEXT: Kind: MEMORY
-; OBJ-NEXT: Memory:
-; OBJ-NEXT: Minimum: 0x0
-; OBJ-NEXT: - Module: env
-; OBJ-NEXT: Field: __indirect_function_table
-; OBJ-NEXT: Kind: TABLE
>From 1b24384c901130deccb300ce819c93ec36eed6da Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sat, 14 Feb 2026 15:53:02 +0900
Subject: [PATCH 11/12] Removal of unnecessary condition checks
---
.../WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 37b58daac644c..1bb9bb4b5bda8 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -69,10 +69,8 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
OS << getMnemonic(*MI).first;
OS << " ";
if (MI->getOperand(TableOperand).isExpr()) {
- if (STI.checkFeatures("+reference-types")) {
- printOperand(MI, TableOperand, OS);
- OS << ", ";
- }
+ printOperand(MI, TableOperand, OS);
+ OS << ", ";
} else {
assert(MI->getOperand(TableOperand).getImm() == 0);
}
>From 55c3f2bb9895cd27c014e5cc5383756933aeb9ea Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sat, 14 Feb 2026 16:12:59 +0900
Subject: [PATCH 12/12] Changing the method for detecting tail calls in
target_features
---
llvm/test/CodeGen/WebAssembly/tailcall.ll | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/test/CodeGen/WebAssembly/tailcall.ll b/llvm/test/CodeGen/WebAssembly/tailcall.ll
index 7f6a3ebc2351d..df2918f3f4611 100644
--- a/llvm/test/CodeGen/WebAssembly/tailcall.ll
+++ b/llvm/test/CodeGen/WebAssembly/tailcall.ll
@@ -582,6 +582,7 @@ define i32 @unique_caller(ptr %p) {
}
; CHECK-LABEL: .section .custom_section.target_features
-; CHECK: .ascii "reference-types"
-; CHECK: .ascii "tail-call"
-; CHECK-LABEL: .section .text.unique_caller
+; CHECK: .ascii "sign-ext"
+; CHECK-NEXT: .int8 43
+; CHECK-NEXT: .int8 9
+; CHECK-NEXT: .ascii "tail-call"
More information about the llvm-commits
mailing list