diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -110,6 +110,12 @@ // address relative the __table_base wasm global. // Only applicable to function symbols. MO_TABLE_BASE_REL, + + // The symbol declares a externref table + MO_SYM_IS_EXTERNREF_TABLE, + + // The symbol declares a funcref table + MO_SYM_IS_FUNCREF_TABLE, }; } // end namespace WebAssemblyII diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -196,6 +196,13 @@ Sym->setGlobalType(wasm::WasmGlobalType{uint8_t(Type), Mutable}); } + // If the GlobalVariable refers to a table, we handle it here instead of + // in emitExternalDecls + if (Sym->isTable()) { + getTargetStreamer()->emitTableType(Sym); + return; + } + emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); if (GV->hasInitializer()) { assert(getSymbolPreferLocal(*GV) == Sym); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -1434,26 +1434,34 @@ return WebAssemblyFrameLowering::getLocalForStackObject(MF, FI->getIndex()); } -static bool IsWebAssemblyTable(SDValue Op) { +// Returns the type of the reference type of the table element or null +// if Op is not a table. +static const Type *IsWebAssemblyTable(SDValue Op) { const GlobalAddressSDNode *GA = dyn_cast(Op); if (GA && WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace())) { const GlobalValue *Value = GA->getGlobal(); const Type *Ty = Value->getValueType(); if (Ty->isArrayTy() && WebAssembly::isRefType(Ty->getArrayElementType())) - return true; + return Ty->getArrayElementType(); } - return false; + return 0; } // This function will accept as Op any access to a table, so Op can // be the actual table or an offset into the table. -static bool IsWebAssemblyTableWithOffset(SDValue Op) { - if (Op->getOpcode() == ISD::ADD && Op->getNumOperands() == 2) - return (Op->getOperand(1).getSimpleValueType() == MVT::i32 && - IsWebAssemblyTableWithOffset(Op->getOperand(0))) || - (Op->getOperand(0).getSimpleValueType() == MVT::i32 && - IsWebAssemblyTableWithOffset(Op->getOperand(1))); +static const Type *IsWebAssemblyTableWithOffset(SDValue Op) { + if (Op->getOpcode() == ISD::ADD && Op->getNumOperands() == 2) { + const Type *Op0TableType = IsWebAssemblyTableWithOffset(Op->getOperand(0)); + if (Op0TableType && Op->getOperand(1).getSimpleValueType() == MVT::i32) + return Op0TableType; + + const Type *Op1TableType = IsWebAssemblyTableWithOffset(Op->getOperand(1)); + if (Op1TableType && Op->getOperand(0).getSimpleValueType() == MVT::i32) + return Op1TableType; + + return 0; + } return IsWebAssemblyTable(Op); } @@ -1516,7 +1524,8 @@ const SDValue &Base = SN->getBasePtr(); const SDValue &Offset = SN->getOffset(); - if (IsWebAssemblyTableWithOffset(Base)) { + if (const Type *TableType = IsWebAssemblyTableWithOffset(Base)) { + if (!Offset->isUndef()) report_fatal_error( "unexpected offset when loading from webassembly table", false); @@ -1528,6 +1537,21 @@ report_fatal_error("failed pattern matching for lowering table store", false); + // GA is a symbol referring to a table, therefore we need to set the + // type to be a table so that the proper .tabletype directives are + // generated. We cannot set TargetFlags on an existing GA, but we can create + // a new GA with the table flags set. + assert(GA->getNumValues() == 1); + GlobalAddressSDNode *NewGA = + cast(DAG.getTargetGlobalAddress( + GA->getGlobal(), DL, GA->getValueType(0), GA->getOffset(), + WebAssembly::isExternrefType(TableType) + ? WebAssemblyII::MO_SYM_IS_EXTERNREF_TABLE + : WebAssemblyII::MO_SYM_IS_FUNCREF_TABLE)); + DAG.ReplaceAllUsesWith(GA, NewGA); + DAG.RemoveDeadNode(GA); + GA = NewGA; + SDVTList Tys = DAG.getVTList(MVT::Other); SDValue TableSetOps[] = {SN->getChain(), SDValue(GA, 0), Idx, Value}; SDValue TableSet = @@ -1568,7 +1592,7 @@ const SDValue &Base = LN->getBasePtr(); const SDValue &Offset = LN->getOffset(); - if (IsWebAssemblyTableWithOffset(Base)) { + if (const Type *TableType = IsWebAssemblyTableWithOffset(Base)) { if (!Offset->isUndef()) report_fatal_error( "unexpected offset when loading from webassembly table", false); @@ -1580,6 +1604,18 @@ report_fatal_error("failed pattern matching for lowering table load", false); + // As in LowerStore, we need to recreate GA with the proper table flags + assert(GA->getNumValues() == 1); + GlobalAddressSDNode *NewGA = + cast(DAG.getTargetGlobalAddress( + GA->getGlobal(), DL, GA->getValueType(0), GA->getOffset(), + WebAssembly::isExternrefType(TableType) + ? WebAssemblyII::MO_SYM_IS_EXTERNREF_TABLE + : WebAssemblyII::MO_SYM_IS_FUNCREF_TABLE)); + DAG.ReplaceAllUsesWith(GA, NewGA); + DAG.RemoveDeadNode(GA); + GA = NewGA; + SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other); SDValue TableGetOps[] = {LN->getChain(), SDValue(GA, 0), Idx}; SDValue TableGet = diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -52,12 +52,9 @@ 0xfc11>; foreach vt = rc.RegTypes in { - def : Pat<(vt (WebAssemblyTableGet (WebAssemblyWrapper tglobaladdr:$table), i32:$idx)), + def : Pat<(vt (WebAssemblyTableGet tglobaladdr:$table, i32:$idx)), (!cast("TABLE_GET_" # rc) tglobaladdr:$table, i32:$idx)>; - def : Pat<(WebAssemblyTableSet - (WebAssemblyWrapper tglobaladdr:$table), - i32:$idx, - vt:$src), + def : Pat<(WebAssemblyTableSet tglobaladdr:$table, i32:$idx, vt:$src), (!cast("TABLE_SET_" # rc) tglobaladdr:$table, i32:$idx, vt:$src)>; } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -120,6 +120,8 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const { MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + bool IsTable = false; + wasm::ValType TableType; unsigned TargetFlags = MO.getTargetFlags(); switch (TargetFlags) { @@ -140,14 +142,22 @@ case WebAssemblyII::MO_TABLE_BASE_REL: Kind = MCSymbolRefExpr::VK_WASM_TBREL; break; + case WebAssemblyII::MO_SYM_IS_EXTERNREF_TABLE: + IsTable = true; + TableType = wasm::ValType::EXTERNREF; + break; + case WebAssemblyII::MO_SYM_IS_FUNCREF_TABLE: + IsTable = true; + TableType = wasm::ValType::FUNCREF; + break; default: llvm_unreachable("Unknown target flag on GV operand"); } const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx); + auto *WasmSym = cast(Sym); if (MO.getOffset() != 0) { - const auto *WasmSym = cast(Sym); if (TargetFlags == WebAssemblyII::MO_GOT) report_fatal_error("GOT symbol references do not support offsets"); if (WasmSym->isFunction()) @@ -163,6 +173,11 @@ Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); } + if (IsTable) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); + WasmSym->setTableType(TableType); + } + return MCOperand::createExpr(Expr); } diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll --- a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll @@ -73,4 +73,4 @@ ret %externref %ref } -; CHECK: .globl externref_table +; CHECK: .tabletype externref_table, externref diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll --- a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll @@ -79,4 +79,4 @@ ret void } -; CHECK: .globl externref_table +; CHECK: .tabletype externref_table, externref diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll @@ -13,7 +13,7 @@ ret void } -; CHECK: .tabletype __funcref_call_table, funcref, 1 +; CHECK: .tabletype __funcref_call_table, funcref, 1 ; CHECK-LABEL: call_funcref_from_table: ; CHECK-NEXT: .functype call_funcref_from_table (i32) -> () @@ -28,6 +28,5 @@ ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: end_function +; CHECK: .tabletype funcref_table, funcref -; CHECK: .globl funcref_table -; CHECK-NEXT .globaltype funcref_table, funcref diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll @@ -72,4 +72,4 @@ ret %funcref %ref } -; CHECK: .globl funcref_table +; CHECK: .tabletype funcref_table, funcref diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll @@ -78,4 +78,4 @@ ret void } -; CHECK: .globl funcref_table +; CHECK: .tabletype funcref_table, funcref