diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -36,6 +36,7 @@ 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 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -31,6 +31,7 @@ ModulePass *createWebAssemblyAddMissingPrototypes(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); +FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv(); // ISel and immediate followup passes. FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, @@ -85,6 +86,7 @@ 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 --- 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 @@ 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 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp @@ -0,0 +1,88 @@ +//=== 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"; + } + + 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::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 = PTI ? 0 : dyn_cast(&*I); + if (!(PTI && (WebAssemblyTargetLowering::isFuncrefType( + PTI->getPointerOperand()->getType()) || + WebAssemblyTargetLowering::isExternrefType( + PTI->getPointerOperand()->getType()))) && + !(ITP && + (WebAssemblyTargetLowering::isFuncrefType(ITP->getDestTy()) || + WebAssemblyTargetLowering::isExternrefType(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 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -332,6 +332,7 @@ 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 @@ 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 --- 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 @@ 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 --- 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 @@ 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 --- 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 @@ "WebAssemblyLowerBrUnless.cpp", "WebAssemblyLowerEmscriptenEHSjLj.cpp", "WebAssemblyLowerGlobalDtors.cpp", + "WebAssemblyLowerRefTypesIntPtrConv.cpp", "WebAssemblyMCInstLower.cpp", "WebAssemblyMCLowerPrePass.cpp", "WebAssemblyMachineFunctionInfo.cpp",