Skip to content

Commit

Permalink
[WebAssembly] Define call-indirect-overlong and bulk-memory-opt featu…
Browse files Browse the repository at this point in the history
…res (#117087)

This defines some new target features. These are subsets of existing
features that reflect implementation concerns:

- "call-indirect-overlong" - implied by "reference-types"; just the
overlong encoding for the `call_indirect` immediate, and not the actual
reference types.

- "bulk-memory-opt" - implied by "bulk-memory": just `memory.copy` and
`memory.fill`, and not the other instructions in the bulk-memory
proposal.

This is split out from #112035.

---------

Co-authored-by: Heejin Ahn <[email protected]>
  • Loading branch information
sunfishcode and aheejin authored Dec 3, 2024
1 parent f71ea4b commit c3536b2
Show file tree
Hide file tree
Showing 34 changed files with 190 additions and 69 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5092,6 +5092,10 @@ def matomics : Flag<["-"], "matomics">, Group<m_wasm_Features_Group>;
def mno_atomics : Flag<["-"], "mno-atomics">, Group<m_wasm_Features_Group>;
def mbulk_memory : Flag<["-"], "mbulk-memory">, Group<m_wasm_Features_Group>;
def mno_bulk_memory : Flag<["-"], "mno-bulk-memory">, Group<m_wasm_Features_Group>;
def mbulk_memory_opt : Flag<["-"], "mbulk-memory-opt">, Group<m_wasm_Features_Group>;
def mno_bulk_memory_opt : Flag<["-"], "mno-bulk-memory-opt">, Group<m_wasm_Features_Group>;
def mcall_indirect_overlong : Flag<["-"], "mcall-indirect-overlong">, Group<m_wasm_Features_Group>;
def mno_call_indirect_overlong : Flag<["-"], "mno-call-indirect-overlong">, Group<m_wasm_Features_Group>;
def mexception_handing : Flag<["-"], "mexception-handling">, Group<m_wasm_Features_Group>;
def mno_exception_handing : Flag<["-"], "mno-exception-handling">, Group<m_wasm_Features_Group>;
def mextended_const : Flag<["-"], "mextended-const">, Group<m_wasm_Features_Group>;
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/Basic/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
return llvm::StringSwitch<bool>(Feature)
.Case("atomics", HasAtomics)
.Case("bulk-memory", HasBulkMemory)
.Case("bulk-memory-opt", HasBulkMemoryOpt)
.Case("call-indirect-overlong", HasCallIndirectOverlong)
.Case("exception-handling", HasExceptionHandling)
.Case("extended-const", HasExtendedConst)
.Case("fp16", HasFP16)
Expand Down Expand Up @@ -79,6 +81,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_atomics__");
if (HasBulkMemory)
Builder.defineMacro("__wasm_bulk_memory__");
if (HasBulkMemoryOpt)
Builder.defineMacro("__wasm_bulk_memory_opt__");
if (HasExceptionHandling)
Builder.defineMacro("__wasm_exception_handling__");
if (HasExtendedConst)
Expand Down Expand Up @@ -155,6 +159,8 @@ bool WebAssemblyTargetInfo::initFeatureMap(
const std::vector<std::string> &FeaturesVec) const {
auto addGenericFeatures = [&]() {
Features["bulk-memory"] = true;
Features["bulk-memory-opt"] = true;
Features["call-indirect-overlong"] = true;
Features["multivalue"] = true;
Features["mutable-globals"] = true;
Features["nontrapping-fptoint"] = true;
Expand Down Expand Up @@ -200,6 +206,22 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasBulkMemory = false;
continue;
}
if (Feature == "+bulk-memory-opt") {
HasBulkMemoryOpt = true;
continue;
}
if (Feature == "-bulk-memory-opt") {
HasBulkMemoryOpt = false;
continue;
}
if (Feature == "+call-indirect-overlong") {
HasCallIndirectOverlong = true;
continue;
}
if (Feature == "-call-indirect-overlong") {
HasCallIndirectOverlong = false;
continue;
}
if (Feature == "+exception-handling") {
HasExceptionHandling = true;
continue;
Expand Down Expand Up @@ -310,6 +332,18 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
<< Feature << "-target-feature";
return false;
}

// bulk-memory-opt is a subset of bulk-memory.
if (HasBulkMemory) {
HasBulkMemoryOpt = true;
}

// The reference-types feature included the change to `call_indirect`
// encodings to support overlong immediates.
if (HasReferenceTypes) {
HasCallIndirectOverlong = true;
}

return true;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {

bool HasAtomics = false;
bool HasBulkMemory = false;
bool HasBulkMemoryOpt = false;
bool HasCallIndirectOverlong = false;
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
bool HasFP16 = false;
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/compress-relocs.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: llvm-mc -mattr=+reference-types -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
; RUN: llvm-mc -mattr=+call-indirect-overlong -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
; RUN: wasm-ld --export-dynamic -o %t.wasm %t2.o %t.o
; RUN: obj2yaml %t.wasm | FileCheck %s
; RUN: wasm-ld --export-dynamic -O2 -o %t-opt.wasm %t2.o %t.o
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/import-table-explicit.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RUN: llvm-mc -mattr=+reference-types -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
# RUN: llvm-mc -mattr=+call-indirect-overlong -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
# RUN: wasm-ld --import-table -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s

Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/invalid-mvp-table-use.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
#
# If any table is defined or declared besides the __indirect_function_table,
# the compilation unit should be compiled with -mattr=+reference-types,
# the compilation unit should be compiled with -mattr=+call-indirect-overlong,
# causing symbol table entries to be emitted for all tables.
# RUN: not wasm-ld --no-entry %t.o -o %t.wasm 2>&1 | FileCheck -check-prefix=CHECK-ERR %s

Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/lto/Inputs/libcall-archive.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ define void @memcpy() #0 {
ret void
}

attributes #0 = { "target-features"="-bulk-memory" }
attributes #0 = { "target-features"="-bulk-memory,-bulk-memory-opt" }
2 changes: 1 addition & 1 deletion lld/test/wasm/lto/libcall-archive.ll
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ entry:

declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)

attributes #0 = { "target-features"="-bulk-memory" }
attributes #0 = { "target-features"="-bulk-memory,-bulk-memory-opt" }

; CHECK: - Type: CUSTOM
; CHECK-NEXT: Name: name
Expand Down
4 changes: 2 additions & 2 deletions lld/test/wasm/lto/stub-library-libcall.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t_main.o %t/main.s
# RUN: llvm-as %S/Inputs/foo.ll -o %t_foo.o
# RUN: llvm-as %S/Inputs/libcall.ll -o %t_libcall.o
# RUN: wasm-ld -mllvm -mattr=-bulk-memory %t_main.o %t_libcall.o %t_foo.o %p/Inputs/stub.so -o %t.wasm
# RUN: wasm-ld -mllvm -mattr=-bulk-memory,-bulk-memory-opt %t_main.o %t_libcall.o %t_foo.o %p/Inputs/stub.so -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s

# The function `func_with_libcall` will generate an undefined reference to
Expand All @@ -12,7 +12,7 @@
# If %t_foo.o is not included in the link we get an undefined symbol reported
# to the dependency of memcpy on the foo export:

# RUN: not wasm-ld -mllvm -mattr=-bulk-memory %t_main.o %t_libcall.o %p/Inputs/stub.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING %s
# RUN: not wasm-ld -mllvm -mattr=-bulk-memory,-bulk-memory-opt %t_main.o %t_libcall.o %p/Inputs/stub.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING %s
# MISSING: stub.so: undefined symbol: foo. Required by memcpy

#--- main.s
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/multi-table.s
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ call_indirect_explicit_tables:
call_indirect table_b, () -> ()
end_function

# RT-MVP: wasm-ld: error: object file not built with 'reference-types' feature conflicts with import of table table_a by file
# RT-MVP: wasm-ld: error: object file not built with 'reference-types' or 'call-indirect-overlong' feature conflicts with import of table table_a by file

# CHECK: --- !WASM
# CHECK-NEXT: FileHeader:
Expand Down
21 changes: 11 additions & 10 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,14 @@ static void setRelocs(const std::vector<T *> &chunks,
}
}

// An object file can have two approaches to tables. With the reference-types
// feature enabled, input files that define or use tables declare the tables
// using symbols, and record each use with a relocation. This way when the
// linker combines inputs, it can collate the tables used by the inputs,
// assigning them distinct table numbers, and renumber all the uses as
// appropriate. At the same time, the linker has special logic to build the
// indirect function table if it is needed.
// An object file can have two approaches to tables. With the
// reference-types feature or call-indirect-overlong feature enabled
// (explicitly, or implied by the reference-types feature), input files that
// define or use tables declare the tables using symbols, and record each use
// with a relocation. This way when the linker combines inputs, it can collate
// the tables used by the inputs, assigning them distinct table numbers, and
// renumber all the uses as appropriate. At the same time, the linker has
// special logic to build the indirect function table if it is needed.
//
// However, MVP object files (those that target WebAssembly 1.0, the "minimum
// viable product" version of WebAssembly) neither write table symbols nor
Expand All @@ -284,9 +285,9 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded(
return;

// It's possible for an input to define tables and also use the indirect
// function table, but forget to compile with -mattr=+reference-types.
// For these newer files, we require symbols for all tables, and
// relocations for all of their uses.
// function table, but forget to compile with -mattr=+call-indirect-overlong
// or -mattr=+reference-types. For these newer files, we require symbols for
// all tables, and relocations for all of their uses.
if (tableSymbolCount != 0) {
error(toString(this) +
": expected one symbol table entry for each of the " +
Expand Down
5 changes: 3 additions & 2 deletions lld/wasm/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,9 @@ void TableSection::addTable(InputTable *table) {
// to assign table number 0 to the indirect function table.
for (const auto *culprit : out.importSec->importedSymbols) {
if (isa<UndefinedTable>(culprit)) {
error("object file not built with 'reference-types' feature "
"conflicts with import of table " +
error("object file not built with 'reference-types' or "
"'call-indirect-overlong' feature conflicts with import of "
"table " +
culprit->getName() + " by file " +
toString(culprit->getFile()));
return;
Expand Down
28 changes: 21 additions & 7 deletions llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,18 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
: MCTargetAsmParser(Options, STI, MII), Parser(Parser),
Lexer(Parser.getLexer()), Is64(STI.getTargetTriple().isArch64Bit()),
TC(Parser, MII, Is64), SkipTypeCheck(Options.MCNoTypeCheck) {
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
FeatureBitset FBS = ComputeAvailableFeatures(STI.getFeatureBits());

// bulk-memory implies bulk-memory-opt
if (FBS.test(WebAssembly::FeatureBulkMemory)) {
FBS.set(WebAssembly::FeatureBulkMemoryOpt);
}
// reference-types implies call-indirect-overlong
if (FBS.test(WebAssembly::FeatureReferenceTypes)) {
FBS.set(WebAssembly::FeatureCallIndirectOverlong);
}

setAvailableFeatures(FBS);
// Don't type check if this is inline asm, since that is a naked sequence of
// instructions without a function/locals decl.
auto &SM = Parser.getSourceManager();
Expand All @@ -291,7 +302,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {

DefaultFunctionTable = getOrCreateFunctionTableSymbol(
getContext(), "__indirect_function_table", Is64);
if (!STI->checkFeatures("+reference-types"))
if (!STI->checkFeatures("+call-indirect-overlong") &&
!STI->checkFeatures("+reference-types"))
DefaultFunctionTable->setOmitFromLinkingSection();
}

Expand Down Expand Up @@ -531,11 +543,13 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
}

bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) {
if (STI->checkFeatures("+reference-types")) {
// If the reference-types feature is enabled, there is an explicit table
// operand. To allow the same assembly to be compiled with or without
// reference types, we allow the operand to be omitted, in which case we
// default to __indirect_function_table.
if (STI->checkFeatures("+call-indirect-overlong") ||
STI->checkFeatures("+reference-types")) {
// If the call-indirect-overlong feature is enabled, or implied by the
// reference-types feature, there is an explicit table operand. To allow
// the same assembly to be compiled with or without
// call-indirect-overlong, we allow the operand to be omitted, in which
// case we default to __indirect_function_table.
auto &Tok = Lexer.getTok();
if (Tok.is(AsmToken::Identifier)) {
auto *Sym =
Expand Down
17 changes: 13 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def FeatureBulkMemory :
SubtargetFeature<"bulk-memory", "HasBulkMemory", "true",
"Enable bulk memory operations">;

def FeatureBulkMemoryOpt :
SubtargetFeature<"bulk-memory-opt", "HasBulkMemoryOpt", "true",
"Enable bulk memory optimization operations">;

def FeatureCallIndirectOverlong :
SubtargetFeature<"call-indirect-overlong", "HasCallIndirectOverlong", "true",
"Enable overlong encoding for call_indirect immediates">;

def FeatureExceptionHandling :
SubtargetFeature<"exception-handling", "HasExceptionHandling", "true",
"Enable Wasm exception handling">;
Expand Down Expand Up @@ -114,15 +122,16 @@ def : ProcessorModel<"mvp", NoSchedModel, []>;
// consideration given to available support in relevant engines and tools, and
// the importance of the features.
def : ProcessorModel<"generic", NoSchedModel,
[FeatureBulkMemory, FeatureMultivalue,
[FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureMultivalue,
FeatureMutableGlobals, FeatureNontrappingFPToInt,
FeatureReferenceTypes, FeatureSignExt]>;

// Latest and greatest experimental version of WebAssembly. Bugs included!
def : ProcessorModel<"bleeding-edge", NoSchedModel,
[FeatureAtomics, FeatureBulkMemory,
FeatureExceptionHandling, FeatureExtendedConst,
FeatureFP16, FeatureMultiMemory,
[FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureExceptionHandling,
FeatureExtendedConst, FeatureFP16, FeatureMultiMemory,
FeatureMultivalue, FeatureMutableGlobals,
FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt,
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
// The table into which this call_indirect indexes.
MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol(
MF->getContext(), Subtarget);
if (Subtarget->hasReferenceTypes()) {
if (Subtarget->hasCallIndirectOverlong()) {
MIB.addSym(Table);
} else {
// Otherwise for the MVP there is at most one table whose number is 0, but
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB,
MF.getContext(), Subtarget)
: WebAssembly::getOrCreateFunctionTableSymbol(
MF.getContext(), Subtarget);
if (Subtarget->hasReferenceTypes()) {
if (Subtarget->hasCallIndirectOverlong()) {
MIB.addSym(Table);
} else {
// For the MVP there is at most one table whose number is 0, but we can't
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
///
//===----------------------------------------------------------------------===//

// Instruction requiring HasBulkMemory and the bulk memory prefix byte
// Instruction requiring HasBulkMemoryOpt and the bulk memory prefix byte
multiclass BULK_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
list<dag> pattern_r, string asmstr_r = "",
string asmstr_s = "", bits<32> simdop = -1> {
defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
!or(0xfc00, !and(0xff, simdop))>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;
}

// Bespoke types and nodes for bulk memory ops
Expand Down Expand Up @@ -89,14 +89,14 @@ defm CPY_A#B : I<(outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx,
rc:$dst, rc:$src, rc:$len
)],
"", "", 0>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;

let usesCustomInserter = 1, isCodeGenOnly = 1, mayStore = 1 in
defm SET_A#B : I<(outs), (ins i32imm_op:$idx, rc:$dst, I32:$value, rc:$size),
(outs), (ins i32imm_op:$idx),
[(wasm_memset (i32 imm:$idx), rc:$dst, I32:$value, rc:$size)],
"", "", 0>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;

}

Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ def HasBulkMemory :
Predicate<"Subtarget->hasBulkMemory()">,
AssemblerPredicate<(all_of FeatureBulkMemory), "bulk-memory">;

def HasBulkMemoryOpt :
Predicate<"Subtarget->hasBulkMemoryOpt()">,
AssemblerPredicate<(all_of FeatureBulkMemoryOpt), "bulk-memory-opt">;

def HasCallIndirectOverlong :
Predicate<"Subtarget->hasCallIndirectOverlong()">,
AssemblerPredicate<(all_of FeatureCallIndirectOverlong), "call-indirect-overlong">;

def HasExceptionHandling :
Predicate<"Subtarget->hasExceptionHandling()">,
AssemblerPredicate<(all_of FeatureExceptionHandling), "exception-handling">;
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemcpy(
SDValue Size, Align Alignment, bool IsVolatile, bool AlwaysInline,
MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const {
auto &ST = DAG.getMachineFunction().getSubtarget<WebAssemblySubtarget>();
if (!ST.hasBulkMemory())
if (!ST.hasBulkMemoryOpt())
return SDValue();

SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32);
Expand Down Expand Up @@ -51,7 +51,7 @@ SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemset(
SDValue Size, Align Alignment, bool IsVolatile, bool AlwaysInline,
MachinePointerInfo DstPtrInfo) const {
auto &ST = DAG.getMachineFunction().getSubtarget<WebAssemblySubtarget>();
if (!ST.hasBulkMemory())
if (!ST.hasBulkMemoryOpt())
return SDValue();

SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32);
Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
CPU = "generic";

ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);

FeatureBitset Bits = getFeatureBits();

// bulk-memory implies bulk-memory-opt
if (HasBulkMemory) {
HasBulkMemoryOpt = true;
Bits.set(WebAssembly::FeatureBulkMemoryOpt);
}

// reference-types implies call-indirect-overlong
if (HasReferenceTypes) {
HasCallIndirectOverlong = true;
Bits.set(WebAssembly::FeatureCallIndirectOverlong);
}

// In case we changed any bits, update `MCSubtargetInfo`'s `FeatureBitset`.
setFeatureBits(Bits);

return *this;
}

Expand Down
Loading

0 comments on commit c3536b2

Please sign in to comment.