Skip to content

Commit 418df4a

Browse files
committed
[WebAssembly] call_indirect issues table number relocs
This patch changes to make call_indirect explicitly refer to the corresponding function table, residualizing TABLE_NUMBER relocs against it. With this change, wasm-ld now sees all references to tables, and can link multiple tables. Differential Revision: https://reviews.llvm.org/D90948
1 parent b86e7ae commit 418df4a

19 files changed

+212
-89
lines changed

lld/test/wasm/call-indirect.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,16 @@ define void @call_ptr(i64 (i64)* %arg) {
122122
; CHECK-NEXT: Body: 42010B
123123
; CHECK-NEXT: - Index: 1
124124
; CHECK-NEXT: Locals:
125-
; CHECK-NEXT: Body: 28028088808000118080808000001A28028488808000118180808000001A0B
125+
; CHECK-NEXT: Body: 2802808880800011808080800080808080001A2802848880800011818080800080808080001A0B
126126
; CHECK-NEXT: - Index: 2
127127
; CHECK-NEXT: Locals:
128128
; CHECK-NEXT: Body: 41020B
129129
; CHECK-NEXT: - Index: 3
130130
; CHECK-NEXT: Locals:
131-
; CHECK-NEXT: Body: 410028028888808000118180808000001A0B
131+
; CHECK-NEXT: Body: 41002802888880800011818080800080808080001A0B
132132
; CHECK-NEXT: - Index: 4
133133
; CHECK-NEXT: Locals:
134-
; CHECK-NEXT: Body: 42012000118280808000001A0B
134+
; CHECK-NEXT: Body: 4201200011828080800080808080001A0B
135135
; CHECK-NEXT: - Type: DATA
136136
; CHECK-NEXT: Segments:
137137
; CHECK-NEXT: - SectionOffset: 7

lld/test/wasm/compress-relocs.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ entry:
2222

2323
; ERROR: wasm-ld: error: --compress-relocations is incompatible with output debug information. Please pass --strip-debug or --strip-all
2424

25-
; CHECK: Body: 28028088808000118080808000001A28028488808000118180808000001A0B
25+
; CHECK: Body: 2802808880800011808080800080808080001A2802848880800011818080800080808080001A0B
2626
; COMPRESS: Body: 280280081100001A280284081101001A0B

lld/test/wasm/shared.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,17 @@ declare void @func_external()
8484
; CHECK-NEXT: GlobalType: I32
8585
; CHECK-NEXT: GlobalMutable: false
8686
; CHECK-NEXT: - Module: env
87-
; CHECK-NEXT: Field: func_external
88-
; CHECK-NEXT: Kind: FUNCTION
89-
; CHECK-NEXT: SigIndex: 1
90-
; CHECK-NEXT: - Module: env
9187
; CHECK-NEXT: Field: __indirect_function_table
9288
; CHECK-NEXT: Kind: TABLE
9389
; CHECK-NEXT: Table:
9490
; CHECK-NEXT: Index: 0
9591
; CHECK-NEXT: ElemType: FUNCREF
9692
; CHECK-NEXT: Limits:
9793
; CHECK-NEXT: Initial: 0x2
94+
; CHECK-NEXT: - Module: env
95+
; CHECK-NEXT: Field: func_external
96+
; CHECK-NEXT: Kind: FUNCTION
97+
; CHECK-NEXT: SigIndex: 1
9898
; CHECK-NEXT: - Module: GOT.mem
9999
; CHECK-NEXT: Field: indirect_func
100100
; CHECK-NEXT: Kind: GLOBAL

llvm/lib/MC/WasmObjectWriter.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,6 @@ void WasmObjectWriter::writeHeader(const MCAssembler &Asm) {
405405

406406
void WasmObjectWriter::executePostLayoutBinding(MCAssembler &Asm,
407407
const MCAsmLayout &Layout) {
408-
// As a stopgap measure until call_indirect instructions start explicitly
409-
// referencing the indirect function table via TABLE_NUMBER relocs, ensure
410-
// that the indirect function table import makes it to the output if anything
411-
// in the compilation unit has caused it to be present.
412-
if (auto *Sym = Asm.getContext().lookupSymbol("__indirect_function_table"))
413-
Asm.registerSymbol(*Sym);
414-
415408
// Build a map of sections to the function that defines them, for use
416409
// in recordRelocation.
417410
for (const MCSymbol &S : Asm.symbols()) {

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,15 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
472472
WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
473473
}
474474

475+
void addFunctionTableOperand(OperandVector &Operands, StringRef TableName,
476+
SMLoc StartLoc, SMLoc EndLoc) {
477+
MCSymbolWasm *Sym = GetOrCreateFunctionTableSymbol(getContext(), TableName);
478+
auto *Val = MCSymbolRefExpr::create(Sym, getContext());
479+
Operands.push_back(std::make_unique<WebAssemblyOperand>(
480+
WebAssemblyOperand::Symbol, StartLoc, EndLoc,
481+
WebAssemblyOperand::SymOp{Val}));
482+
}
483+
475484
bool ParseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,
476485
SMLoc NameLoc, OperandVector &Operands) override {
477486
// Note: Name does NOT point into the sourcecode, but to a local, so
@@ -508,6 +517,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
508517
bool ExpectBlockType = false;
509518
bool ExpectFuncType = false;
510519
bool ExpectHeapType = false;
520+
bool ExpectFunctionTable = false;
511521
if (Name == "block") {
512522
push(Block);
513523
ExpectBlockType = true;
@@ -547,15 +557,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
547557
return true;
548558
} else if (Name == "call_indirect" || Name == "return_call_indirect") {
549559
ExpectFuncType = true;
550-
// Ensure that the object file has a __indirect_function_table import, as
551-
// we call_indirect against it.
552-
auto &Ctx = getStreamer().getContext();
553-
MCSymbolWasm *Sym =
554-
GetOrCreateFunctionTableSymbol(Ctx, "__indirect_function_table");
555-
// Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
556-
// it as NO_STRIP so as to ensure that the indirect function table makes
557-
// it to linked output.
558-
Sym->setNoStrip();
560+
ExpectFunctionTable = true;
559561
} else if (Name == "ref.null") {
560562
ExpectHeapType = true;
561563
}
@@ -571,7 +573,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
571573
return true;
572574
// Got signature as block type, don't need more
573575
ExpectBlockType = false;
574-
auto &Ctx = getStreamer().getContext();
576+
auto &Ctx = getContext();
575577
// The "true" here will cause this to be a nameless symbol.
576578
MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);
577579
auto *WasmSym = cast<MCSymbolWasm>(Sym);
@@ -583,6 +585,16 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
583585
Operands.push_back(std::make_unique<WebAssemblyOperand>(
584586
WebAssemblyOperand::Symbol, Loc.getLoc(), Loc.getEndLoc(),
585587
WebAssemblyOperand::SymOp{Expr}));
588+
589+
// Allow additional operands after the signature, notably for
590+
// call_indirect against a named table.
591+
if (Lexer.isNot(AsmToken::EndOfStatement)) {
592+
if (expect(AsmToken::Comma, ","))
593+
return true;
594+
if (Lexer.is(AsmToken::EndOfStatement)) {
595+
return error("Unexpected trailing comma");
596+
}
597+
}
586598
}
587599

588600
while (Lexer.isNot(AsmToken::EndOfStatement)) {
@@ -608,8 +620,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
608620
WebAssemblyOperand::Integer, Id.getLoc(), Id.getEndLoc(),
609621
WebAssemblyOperand::IntOp{static_cast<int64_t>(HeapType)}));
610622
Parser.Lex();
623+
} else if (ExpectFunctionTable) {
624+
addFunctionTableOperand(Operands, Id.getString(), Id.getLoc(),
625+
Id.getEndLoc());
626+
Parser.Lex();
611627
} else {
612-
// Assume this identifier is a label.
613628
const MCExpr *Val;
614629
SMLoc End;
615630
if (Parser.parseExpression(Val, End))
@@ -674,6 +689,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
674689
// Support blocks with no operands as default to void.
675690
addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void);
676691
}
692+
if (ExpectFunctionTable && Operands.size() == 2) {
693+
// If call_indirect doesn't specify a target table, supply one.
694+
addFunctionTableOperand(Operands, "__indirect_function_table", NameLoc,
695+
SMLoc::getFromPointer(Name.end()));
696+
}
677697
Parser.Lex();
678698
return false;
679699
}

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
6868
for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) {
6969
if (MI->getOpcode() == WebAssembly::CALL_INDIRECT &&
7070
I - Start == NumVariadicDefs) {
71-
// Skip type and flags arguments when printing for tests
71+
// Skip type and table arguments when printing for tests.
7272
++I;
7373
continue;
7474
}

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,16 @@ inline bool isCallIndirect(unsigned Opc) {
401401
}
402402
}
403403

404+
inline bool isRetCallIndirect(unsigned Opc) {
405+
switch (Opc) {
406+
case WebAssembly::RET_CALL_INDIRECT:
407+
case WebAssembly::RET_CALL_INDIRECT_S:
408+
return true;
409+
default:
410+
return false;
411+
}
412+
}
413+
404414
inline bool isBrTable(const MachineInstr &MI) {
405415
switch (MI.getOpcode()) {
406416
case WebAssembly::BR_TABLE_I32:

llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -869,18 +869,11 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
869869
if (IsDirect) {
870870
MIB.addGlobalAddress(Func);
871871
} else {
872-
// Add placeholders for the type index and immediate flags
872+
// Placehoder for the type index.
873873
MIB.addImm(0);
874-
MIB.addImm(0);
875-
876-
// Ensure that the object file has a __indirect_function_table import, as we
877-
// call_indirect against it.
878-
MCSymbolWasm *Sym = WebAssembly::getOrCreateFunctionTableSymbol(
879-
MF->getMMI().getContext(), "__indirect_function_table");
880-
// Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
881-
// it as NO_STRIP so as to ensure that the indirect function table makes it
882-
// to linked output.
883-
Sym->setNoStrip();
874+
// The table into which this call_indirect indexes.
875+
MIB.addSym(WebAssembly::getOrCreateFunctionTableSymbol(
876+
MF->getMMI().getContext(), "__indirect_function_table"));
884877
}
885878

886879
for (unsigned ArgReg : Args)

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -475,19 +475,12 @@ static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults,
475475
for (auto Def : CallResults.defs())
476476
MIB.add(Def);
477477

478-
// Add placeholders for the type index and immediate flags
479478
if (IsIndirect) {
479+
// Placehoder for the type index.
480480
MIB.addImm(0);
481-
MIB.addImm(0);
482-
483-
// Ensure that the object file has a __indirect_function_table import, as we
484-
// call_indirect against it.
485-
MCSymbolWasm *Sym = WebAssembly::getOrCreateFunctionTableSymbol(
486-
MF.getContext(), "__indirect_function_table");
487-
// Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
488-
// it as NO_STRIP so as to ensure that the indirect function table makes it
489-
// to linked output.
490-
Sym->setNoStrip();
481+
// The table into which this call_indirect indexes.
482+
MIB.addSym(WebAssembly::getOrCreateFunctionTableSymbol(
483+
MF.getContext(), "__indirect_function_table"));
491484
}
492485

493486
for (auto Use : CallParams.uses())

llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ defm RET_CALL_RESULTS :
4848
I<(outs), (ins variable_ops), (outs), (ins), [],
4949
"return_call_results", "return_call_results", -1>;
5050

51+
// Note that instructions with variable_ops have custom printers in
52+
// WebAssemblyInstPrinter.cpp.
53+
5154
let variadicOpsAreDefs = 1 in
5255
defm CALL :
5356
I<(outs), (ins function32_op:$callee, variable_ops),
@@ -56,9 +59,12 @@ defm CALL :
5659

5760
let variadicOpsAreDefs = 1 in
5861
defm CALL_INDIRECT :
59-
I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
60-
(outs), (ins TypeIndex:$type, i32imm:$flags), [],
61-
"call_indirect", "call_indirect\t$type", 0x11>;
62+
I<(outs),
63+
(ins TypeIndex:$type, table32_op:$table, variable_ops),
64+
(outs),
65+
(ins TypeIndex:$type, table32_op:$table),
66+
[],
67+
"call_indirect", "call_indirect\t$type, $table", 0x11>;
6268

6369
let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in
6470
defm RET_CALL :
@@ -69,9 +75,9 @@ defm RET_CALL :
6975

7076
let isReturn = 1 in
7177
defm RET_CALL_INDIRECT :
72-
I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
73-
(outs), (ins TypeIndex:$type, i32imm:$flags), [],
74-
"return_call_indirect\t", "return_call_indirect\t$type",
78+
I<(outs), (ins TypeIndex:$type, table32_op:$table, variable_ops),
79+
(outs), (ins TypeIndex:$type, table32_op:$table), [],
80+
"return_call_indirect\t", "return_call_indirect\t$type, $table",
7581
0x13>,
7682
Requires<[HasTailCall]>;
7783

llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
161161
report_fatal_error("Global indexes with offsets not supported");
162162
if (WasmSym->isEvent())
163163
report_fatal_error("Event indexes with offsets not supported");
164+
if (WasmSym->isTable())
165+
report_fatal_error("Table indexes with offsets not supported");
164166

165167
Expr = MCBinaryExpr::createAdd(
166168
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
@@ -259,7 +261,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
259261

260262
// return_call_indirect instructions have the return type of the
261263
// caller
262-
if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)
264+
if (WebAssembly::isRetCallIndirect(MI->getOpcode()))
263265
getFunctionReturns(MI, Returns);
264266

265267
MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));

llvm/test/CodeGen/WebAssembly/function-pointer64.ll

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ entry:
3434
; CHECK-NEXT: i32.const 1
3535
; CHECK-NEXT: local.get 0
3636
; CHECK-NEXT: i32.wrap_i64
37-
; CHECK-NEXT: call_indirect (i32) -> ()
37+
; CHECK-NEXT: call_indirect (i32) -> (), __indirect_function_table
3838

3939
; CHECK: .functype test () -> ()
4040
; CHECK-NEXT: i64.const bar
4141
; CHECK-NEXT: call foo
4242

4343

44-
; Check we're emitting a 64-bit reloc for `i64.const bar` and the global.
44+
; Check we're emitting a 64-bit relocs for the call_indirect, the
45+
; `i64.const bar` reference in code, and the global.
4546

4647
; YAML: Memory:
4748
; YAML-NEXT: Flags: [ IS_64 ]
@@ -50,7 +51,10 @@ entry:
5051
; YAML: - Type: CODE
5152
; YAML: - Type: R_WASM_TABLE_INDEX_SLEB64
5253
; YAML-NEXT: Index: 0
53-
; YAML-NEXT: Offset: 0x16
54+
; YAML-NEXT: Offset: 0x1A
55+
; YAML: - Type: R_WASM_TABLE_INDEX_SLEB64
56+
; YAML-NEXT: Index: 0
57+
; YAML-NEXT: Offset: 0x2D
5458

5559
; YAML: - Type: DATA
5660
; YAML: - Type: R_WASM_TABLE_INDEX_I64

llvm/test/CodeGen/WebAssembly/multivalue.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ define %pair @pair_call_return() {
5757
; CHECK-LABEL: pair_call_indirect:
5858
; CHECK-NEXT: .functype pair_call_indirect (i32) -> (i32, i64)
5959
; CHECK-NEXT: local.get 0{{$}}
60-
; CHECK-NEXT: call_indirect () -> (i32, i64){{$}}
60+
; CHECK-NEXT: call_indirect () -> (i32, i64), __indirect_function_table{{$}}
6161
; CHECK-NEXT: end_function{{$}}
6262
; REGS: call_indirect $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
6363
define %pair @pair_call_indirect(%pair()* %f) {

llvm/test/MC/WebAssembly/basic-assembly.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ empty_fref_table:
153153
# CHECK-NEXT: i64.const 1234
154154
# CHECK-NEXT: call something2
155155
# CHECK-NEXT: i32.const 0
156-
# CHECK-NEXT: call_indirect (i32, f64) -> ()
156+
# CHECK-NEXT: call_indirect (i32, f64) -> (), __indirect_function_table
157157
# CHECK-NEXT: i32.const 1
158158
# CHECK-NEXT: i32.add
159159
# CHECK-NEXT: local.tee 0

0 commit comments

Comments
 (0)