diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index 5ce8df82c8445d..c8216aacb59e85 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -36,6 +36,7 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyLowerBrUnless.cpp WebAssemblyLowerEmscriptenEHSjLj.cpp WebAssemblyLowerGlobalDtors.cpp + WebAssemblyLowerRefTypesIntPtrConv.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp WebAssemblyMCLowerPrePass.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index 9eb960d018d3f2..aefca2e6120e4e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -31,6 +31,7 @@ ModulePass *createWebAssemblyLowerGlobalDtors(); ModulePass *createWebAssemblyAddMissingPrototypes(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); +FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv(); // ISel and immediate followup passes. FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, @@ -85,6 +86,7 @@ void initializeWebAssemblyRegNumberingPass(PassRegistry &); void initializeWebAssemblyDebugFixupPass(PassRegistry &); void initializeWebAssemblyPeepholePass(PassRegistry &); void initializeWebAssemblyMCLowerPrePassPass(PassRegistry &); +void initializeWebAssemblyLowerRefTypesIntPtrConvPass(PassRegistry &); namespace WebAssembly { enum TargetIndex { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index f4bae59132e68b..804b41b467b5a9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" + using namespace llvm; #define DEBUG_TYPE "wasm-isel" @@ -48,32 +49,11 @@ class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { return "WebAssembly Instruction Selection"; } - void checkForInvalidNodes(const Function &F) { - // This function will check for uses of ptrtoint on reference types and - // report a fatal error if these are found. - for (const BasicBlock &BB : F) { - for (const Instruction &I : BB) { - if (const PtrToIntInst *PTI = dyn_cast(&I)) { - const Value *V = PTI->getPointerOperand(); - if (WebAssemblyTargetLowering::isFuncrefType(V->getType()) || - WebAssemblyTargetLowering::isExternrefType(V->getType())) - report_fatal_error("ptrtoint not allowed on reference types"); - } else if (const IntToPtrInst *ITP = dyn_cast(&I)) { - if (WebAssemblyTargetLowering::isFuncrefType(ITP->getDestTy()) || - WebAssemblyTargetLowering::isExternrefType(ITP->getDestTy())) - report_fatal_error("inttoptr not allowed on reference types"); - } - } - } - } - bool runOnMachineFunction(MachineFunction &MF) override { LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" "********** Function: " << MF.getName() << '\n'); - checkForInvalidNodes(MF.getFunction()); - Subtarget = &MF.getSubtarget(); return SelectionDAGISel::runOnMachineFunction(MF); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp new file mode 100644 index 00000000000000..55fdbb4b2719f5 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp @@ -0,0 +1,90 @@ +//=== WebAssemblyLowerRefTypesIntPtrConv.cpp - +// Lower IntToPtr and PtrToInt on Reference Types ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Lowers IntToPtr and PtrToInt instructions on reference types to +/// Trap instructions since they have been allowed to operate +/// on non-integral pointers. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssembly.h" +#include "WebAssemblySubtarget.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Pass.h" + +using namespace llvm; + +#define DEBUG_TYPE "wasm-lower-reftypes-intptr-conv" + +namespace { +class WebAssemblyLowerRefTypesIntPtrConv final : public FunctionPass { + StringRef getPassName() const override { + return "WebAssembly Lower RefTypes Int-Ptr Conversions"; + } + + static bool isRefType(Type *T); + + bool runOnFunction(Function &MF) override; + +public: + static char ID; // Pass identification + WebAssemblyLowerRefTypesIntPtrConv() : FunctionPass(ID) {} +}; +} // end anonymous namespace + +char WebAssemblyLowerRefTypesIntPtrConv::ID = 0; +INITIALIZE_PASS(WebAssemblyLowerRefTypesIntPtrConv, DEBUG_TYPE, + "WebAssembly Lower RefTypes Int-Ptr Conversions", false, false) + +FunctionPass *llvm::createWebAssemblyLowerRefTypesIntPtrConv() { + return new WebAssemblyLowerRefTypesIntPtrConv(); +} + +bool WebAssemblyLowerRefTypesIntPtrConv::isRefType(Type *T) { + return WebAssemblyTargetLowering::isFuncrefType(T) || + WebAssemblyTargetLowering::isExternrefType(T); +} + +bool WebAssemblyLowerRefTypesIntPtrConv::runOnFunction(Function &F) { + LLVM_DEBUG(dbgs() << "********** Lower RefTypes IntPtr Convs **********\n" + "********** Function: " + << F.getName() << '\n'); + + // This function will check for uses of ptrtoint and inttoptr on reference + // types and replace them with a trap instruction. + // + // We replace the instruction by a trap instruction + // and its uses by null in the case of inttoptr and 0 in the + // case of ptrtoint. + std::set worklist; + + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + PtrToIntInst *PTI = dyn_cast(&*I); + IntToPtrInst *ITP = dyn_cast(&*I); + if (!(PTI && isRefType(PTI->getPointerOperand()->getType())) && + !(ITP && isRefType(ITP->getDestTy()))) + continue; + + UndefValue *U = UndefValue::get(I->getType()); + I->replaceAllUsesWith(U); + + Function *TrapIntrin = + Intrinsic::getDeclaration(F.getParent(), Intrinsic::trap); + CallInst::Create(TrapIntrin, {}, "", &*I); + + worklist.insert(&*I); + } + + // erase each instruction replaced by trap + for (Instruction *I : worklist) + I->eraseFromParent(); + + return !worklist.empty(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 746a7599c58ce2..10497e45b4bf92 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -332,6 +332,7 @@ class WebAssemblyPassConfig final : public TargetPassConfig { void addPostRegAlloc() override; bool addGCPasses() override { return false; } void addPreEmitPass() override; + bool addPreISel() override; // No reg alloc bool addRegAssignAndRewriteFast() override { return false; } @@ -518,6 +519,12 @@ void WebAssemblyPassConfig::addPreEmitPass() { addPass(createWebAssemblyMCLowerPrePass()); } +bool WebAssemblyPassConfig::addPreISel() { + TargetPassConfig::addPreISel(); + addPass(createWebAssemblyLowerRefTypesIntPtrConv()); + return false; +} + yaml::MachineFunctionInfo * WebAssemblyTargetMachine::createDefaultFuncInfoYAML() const { return new yaml::WebAssemblyFunctionInfo(); diff --git a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll index cc106b5dee32e3..252dbfe12040d4 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll @@ -1,4 +1,4 @@ -; RUN: not --crash llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types 2>&1 | FileCheck %s %extern = type opaque %externref = type %extern addrspace(10)* @@ -8,4 +8,10 @@ define %externref @int_to_externref(i32 %i) { ret %externref %ref } -; CHECK-ERROR: LLVM ERROR: inttoptr not allowed on reference types + +; CHECK-LABEL: int_to_externref: +; CHECK-NEXT: .functype int_to_externref (i32) -> (externref) +; CHECK-NEXT: .local externref +; CHECK-NEXT: unreachable +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: end_function diff --git a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll index 8aea1506a903e6..648bfc510a5491 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll @@ -1,4 +1,4 @@ -; RUN: not --crash llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types 2>&1 | FileCheck %s %extern = type opaque %externref = type %extern addrspace(10)* @@ -8,4 +8,9 @@ define i32 @externref_to_int(%externref %ref) { ret i32 %i } -; CHECK-ERROR: LLVM ERROR: ptrtoint not allowed on reference types +; CHECK-LABEL: externref_to_int: +; CHECK-NEXT: .functype externref_to_int (externref) -> (i32) +; CHECK-NEXT: .local i32 +; CHECK-NEXT: unreachable +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: end_function diff --git a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn index 39cfa79e15663c..4710c08d75b83a 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn @@ -52,6 +52,7 @@ static_library("LLVMWebAssemblyCodeGen") { "WebAssemblyLowerBrUnless.cpp", "WebAssemblyLowerEmscriptenEHSjLj.cpp", "WebAssemblyLowerGlobalDtors.cpp", + "WebAssemblyLowerRefTypesIntPtrConv.cpp", "WebAssemblyMCInstLower.cpp", "WebAssemblyMCLowerPrePass.cpp", "WebAssemblyMachineFunctionInfo.cpp",