Index: lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp =================================================================== --- lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -93,6 +93,7 @@ const MCOperandInfo &Info = Desc.OpInfo[i]; switch (Info.OperandType) { case MCOI::OPERAND_IMMEDIATE: + case WebAssembly::OPERAND_TABLE: case WebAssembly::OPERAND_P2ALIGN: case WebAssembly::OPERAND_BASIC_BLOCK: { if (Pos + sizeof(uint64_t) > Bytes.size()) Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -49,7 +49,9 @@ /// 64-bit floating-point immediates. OPERAND_FP64IMM, /// p2align immediate for load and store address alignment. - OPERAND_P2ALIGN + OPERAND_P2ALIGN, + /// table immediate to specify which indirect call table to use. + OPERAND_TABLE }; /// WebAssembly-specific directive identifiers. Index: lib/Target/WebAssembly/WebAssemblyFastISel.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -700,6 +700,17 @@ if (!IsVoid) MIB.addReg(ResultReg, RegState::Define); + // Add the table operand, default to zero + if (!IsDirect) { + if (MDNode *MD = Call->getMetadata("wasm.index")) + MIB.addImm(cast(MD->getOperand(0)) + ->getValue() + ->getUniqueInteger() + .getSExtValue()); + else + MIB.addImm(0); + } + if (IsDirect) MIB.addGlobalAddress(Func); else @@ -1102,7 +1113,7 @@ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)) .addMBB(TBB) .addReg(CondReg); - + finishCondBranch(Br->getParent(), TBB, FBB); return true; } Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -390,7 +390,19 @@ // Compute the operands for the CALLn node. SmallVector Ops; + // Add the indirect call table operand, default to zero + APInt TableOp; Ops.push_back(Chain); + if (CLI.CS && !CLI.CS->getCalledFunction()) { + if (MDNode *MD = CLI.CS->getInstruction()->getMetadata("wasm.index")) { + TableOp = cast(MD->getOperand(0)) + ->getValue() + ->getUniqueInteger(); + } else { + TableOp = APInt::getNullValue(64); + } + Ops.push_back(DAG.getConstant(TableOp, DL, MVT::i64)); + } Ops.push_back(Callee); // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs Index: lib/Target/WebAssembly/WebAssemblyInstrCall.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -29,9 +29,10 @@ def CALL_#vt : I<(outs vt:$dst), (ins i32imm:$callee, variable_ops), [(set vt:$dst, (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee")>; - def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops), - [(set vt:$dst, (WebAssemblycall1 I32:$callee))], - !strconcat(prefix, "call_indirect\t$dst, $callee")>; + def CALL_INDIRECT_#vt : I<(outs vt:$dst), + (ins table_op:$table, I32:$callee, variable_ops), + [(set vt:$dst, (WebAssemblycall1 imm:$table, I32:$callee))], + !strconcat(prefix, "call_indirect.$table\t$dst, $callee")>; } let Uses = [SP32, SP64], isCall = 1 in { defm : CALL; @@ -41,10 +42,11 @@ def CALL_VOID : I<(outs), (ins i32imm:$callee, variable_ops), [(WebAssemblycall0 (i32 imm:$callee))], - "call \t$callee">; - def CALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops), - [(WebAssemblycall0 I32:$callee)], - "call_indirect\t$callee">; + "call\t$callee">; + def CALL_INDIRECT_VOID : I<(outs), + (ins table_op:$table, I32:$callee, variable_ops), + [(WebAssemblycall0 imm:$table, I32:$callee)], + "call_indirect.$table\t$callee">; } // Uses = [SP32,SP64], isCall = 1 } // Defs = [ARGUMENTS] Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -83,6 +83,9 @@ } } // OperandType = "OPERAND_P2ALIGN" +let OperandType = "OPERAND_TABLE" in +def table_op : Operand; + } // OperandNamespace = "WebAssembly" //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyRegStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -304,7 +304,7 @@ // Ask LiveIntervals whether moving this virtual register use or def to // Insert will change which value numbers are seen. - // + // // If the operand is a use of a register that is also defined in the same // instruction, test that the newly defined value reaches the insert point, // since the operand will be moving along with the def. @@ -385,7 +385,7 @@ // // This is needed as a consequence of using implicit get_locals for // uses and implicit set_locals for defs. - if (UseInst->getDesc().getNumDefs() == 0) + if (UseInst->getDesc().getNumDefs() == 0) return false; const MachineOperand &MO = UseInst->getOperand(0); if (!MO.isReg()) Index: lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- lib/Transforms/IPO/LowerTypeTests.cpp +++ lib/Transforms/IPO/LowerTypeTests.cpp @@ -288,7 +288,8 @@ Int64Ty = Type::getInt64Ty(M->getContext()); IntPtrTy = DL.getIntPtrType(M->getContext(), 0); - IndirectIndex = 0; + // In WebAssembly, the default table is at index 0, so start creating at 1 + IndirectIndex = 1; TypeTestCallSites.clear(); @@ -828,9 +829,10 @@ ConstantArray::get(JumpTableType, JumpTableEntries)); } -// Assign a dummy layout based using an incrementing counter, and store this as -// metadata. During generation of the indexed indirect function call table, the -// backend will ensure that the appropriate indexes are used. +// Assign each disjoint set to a different indirect function call table using +// an incrementing counter. Then, eliminate explicit type tests. This is safe +// because the type homogeneity of each disjoint set is checked at load-time. +// Additionally, calls to out of bounds table entries will trap at runtime. void LowerTypeTests::buildBitSetsFromFunctionsWASM( ArrayRef TypeIds, ArrayRef Functions) { assert(!Functions.empty()); @@ -847,15 +849,50 @@ ArrayRef(ConstantAsMetadata::get( ConstantInt::get(Int64Ty, IndirectIndex)))); F->setMetadata("wasm.index", MD); + } + + // Tag the indirect call with the table index, and eliminate the explicit type test. + for (Metadata *TypeId : TypeIds) { + for (CallInst *TypeTest : TypeTestCallSites[TypeId]) { + // Find the branch statement immediately following the bitset test call. + BranchInst *BI = nullptr; + for (Use &U : TypeTest->uses()) { + if ((isa(U.getUser()))) { + BI = cast(U.getUser()); + break; + } + } + + // Find the original indirect call in the 'cont' block. + CallInst *CI = nullptr; + if (BI && BI->getNumSuccessors() > 0) { + for (Instruction &I : *BI->getSuccessor(0)) { + if ((isa(I)) && !cast(I).getCalledFunction()) { + CI = &cast(I); + break; + } + } + } - GlobalLayout[F] = IndirectIndex++; + // Tag the indirect call with the table index + if (CI) { + MDNode *MD = MDNode::get(CI->getContext(), + ArrayRef(ConstantAsMetadata::get( + ConstantInt::get(Int64Ty, IndirectIndex)))); + CI->setMetadata("wasm.index", MD); + } + + // Replace each type test with true, and let simplifycfg remove the branch. + ConstantInt *True = ConstantInt::getTrue(TypeTest->getContext()); + TypeTest->replaceAllUsesWith(True); + TypeTest->eraseFromParent(); + + ++NumTypeTestCallsLowered; + } } - // The indirect function table index space starts at zero, so pass a NULL - // pointer as the subtracted "jump table" offset. - lowerTypeTestCalls(TypeIds, - ConstantPointerNull::get(cast(Int32PtrTy)), - GlobalLayout); + // Increment the counter to put each disjoint set in a different table. + IndirectIndex += 1; } void LowerTypeTests::buildBitSetsFromDisjointSet( Index: test/CodeGen/WebAssembly/call.ll =================================================================== --- test/CodeGen/WebAssembly/call.ll +++ test/CodeGen/WebAssembly/call.ll @@ -80,7 +80,7 @@ ; CHECK-LABEL: call_indirect_void: ; CHECK-NEXT: .param i32{{$}} -; CHECK-NEXT: {{^}} call_indirect $0{{$}} +; CHECK-NEXT: {{^}} call_indirect.0 $0{{$}} ; CHECK-NEXT: return{{$}} define void @call_indirect_void(void ()* %callee) { call void %callee() @@ -90,7 +90,7 @@ ; CHECK-LABEL: call_indirect_i32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: {{^}} i32.call_indirect $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: {{^}} i32.call_indirect.0 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @call_indirect_i32(i32 ()* %callee) { %t = call i32 %callee() Index: test/CodeGen/WebAssembly/cfi.ll =================================================================== --- test/CodeGen/WebAssembly/cfi.ll +++ test/CodeGen/WebAssembly/cfi.ll @@ -12,13 +12,13 @@ } ; CHECK-LABEL: f: -; CHECK: .indidx 0 +; CHECK: .indidx 1 define void @f() !type !0 { ret void } ; CHECK-LABEL: g: -; CHECK: .indidx 1 +; CHECK: .indidx 2 define void @g() !type !1 { ret void } @@ -30,9 +30,8 @@ declare void @llvm.trap() nounwind noreturn ; CHECK-LABEL: foo: -; CHECK: br_if -; CHECK: br_if -; CHECK: unreachable +; CHECK-NOT: br_if +; CHECK-NOT: unreachable define i1 @foo(i8* %p) { call void @f() call void @g() Index: test/Transforms/LowerTypeTests/function-disjoint.ll =================================================================== --- test/Transforms/LowerTypeTests/function-disjoint.ll +++ test/Transforms/LowerTypeTests/function-disjoint.ll @@ -33,14 +33,16 @@ call void @f() call void @g() ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to i64) - ; WASM32: icmp eq i64 {{.*}}, 0 + ; WASM32-NOT: icmp ult i64 {{.*}}, 0 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to i64) - ; WASM32: icmp eq i64 {{.*}}, 1 + ; WASM32-NOT: icmp ult i64 {{.*}}, 1 %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2") %z = add i1 %x, %y + ; WASM32-NOT: br i1 {{.*}} label %cont, label %trap + ; WASM32: add i1 true, true ret i1 %z } -; WASM32: ![[I0]] = !{i64 0} -; WASM32: ![[I1]] = !{i64 1} +; WASM32: ![[I0]] = !{i64 1} +; WASM32: ![[I1]] = !{i64 2} Index: test/Transforms/LowerTypeTests/function-ext.ll =================================================================== --- test/Transforms/LowerTypeTests/function-ext.ll +++ test/Transforms/LowerTypeTests/function-ext.ll @@ -12,8 +12,9 @@ define i1 @bar(i8* %ptr) { ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) - ; WASM32: sub i64 {{.*}}, 0 - ; WASM32: icmp ult i64 {{.*}}, 1 + ; WASM32-NOT: sub i64 {{.*}}, 0 + ; WASM32-NOT: icmp ult i64 {{.*}}, 1 + ; WASM32: ret i1 true %p = call i1 @llvm.type.test(i8* %ptr, metadata !"void") ret i1 %p } Index: test/Transforms/LowerTypeTests/function.ll =================================================================== --- test/Transforms/LowerTypeTests/function.ll +++ test/Transforms/LowerTypeTests/function.ll @@ -12,13 +12,13 @@ ; X64: @g = alias void (), bitcast (<{ i8, i32, i8, i8, i8 }>* getelementptr inbounds ([2 x <{ i8, i32, i8, i8, i8 }>], [2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]], i64 0, i64 1) to void ()*) ; X64: define private void @[[FNAME]]() -; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]] +; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I:[0-9]+]] define void @f() !type !0 { ret void } ; X64: define private void @[[GNAME]]() -; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I1:[0-9]+]] +; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I]] define void @g() !type !0 { ret void } @@ -31,11 +31,12 @@ call void @f() call void @g() ; X64: sub i64 {{.*}}, ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) - ; WASM32: sub i64 {{.*}}, 0 - ; WASM32: icmp ult i64 {{.*}}, 2 + ; WASM32-NOT: sub i64 {{.*}}, 0 + ; WASM32-NOT: icmp ult i64 {{.*}}, 2 + ; WASM32-NOT: br i1 {{.*}} label %cont, label %trap + ; WASM32: ret i1 true %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") ret i1 %x } -; WASM32: ![[I0]] = !{i64 0} -; WASM32: ![[I1]] = !{i64 1} +; WASM32: ![[I]] = !{i64 1}