WebAssemblyAddMissingPrototypes.cpp revision 355940
1//===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8/// 9/// \file 10/// Add prototypes to prototypes-less functions. 11/// 12/// WebAssembly has strict function prototype checking so we need functions 13/// declarations to match the call sites. Clang treats prototype-less functions 14/// as varargs (foo(...)) which happens to work on existing platforms but 15/// doesn't under WebAssembly. This pass will find all the call sites of each 16/// prototype-less function, ensure they agree, and then set the signature 17/// on the function declaration accordingly. 18/// 19//===----------------------------------------------------------------------===// 20 21#include "WebAssembly.h" 22#include "llvm/IR/Constants.h" 23#include "llvm/IR/IRBuilder.h" 24#include "llvm/IR/Module.h" 25#include "llvm/IR/Operator.h" 26#include "llvm/Pass.h" 27#include "llvm/Support/Debug.h" 28#include "llvm/Transforms/Utils/Local.h" 29#include "llvm/Transforms/Utils/ModuleUtils.h" 30using namespace llvm; 31 32#define DEBUG_TYPE "wasm-add-missing-prototypes" 33 34namespace { 35class WebAssemblyAddMissingPrototypes final : public ModulePass { 36 StringRef getPassName() const override { 37 return "Add prototypes to prototypes-less functions"; 38 } 39 40 void getAnalysisUsage(AnalysisUsage &AU) const override { 41 AU.setPreservesCFG(); 42 ModulePass::getAnalysisUsage(AU); 43 } 44 45 bool runOnModule(Module &M) override; 46 47public: 48 static char ID; 49 WebAssemblyAddMissingPrototypes() : ModulePass(ID) {} 50}; 51} // End anonymous namespace 52 53char WebAssemblyAddMissingPrototypes::ID = 0; 54INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE, 55 "Add prototypes to prototypes-less functions", false, false) 56 57ModulePass *llvm::createWebAssemblyAddMissingPrototypes() { 58 return new WebAssemblyAddMissingPrototypes(); 59} 60 61bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) { 62 LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n"); 63 64 std::vector<std::pair<Function *, Function *>> Replacements; 65 66 // Find all the prototype-less function declarations 67 for (Function &F : M) { 68 if (!F.isDeclaration() || !F.hasFnAttribute("no-prototype")) 69 continue; 70 71 LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName() 72 << "\n"); 73 74 // When clang emits prototype-less C functions it uses (...), i.e. varargs 75 // function that take no arguments (have no sentinel). When we see a 76 // no-prototype attribute we expect the function have these properties. 77 if (!F.isVarArg()) 78 report_fatal_error( 79 "Functions with 'no-prototype' attribute must take varargs: " + 80 F.getName()); 81 unsigned NumParams = F.getFunctionType()->getNumParams(); 82 if (NumParams != 0) { 83 if (!(NumParams == 1 && F.arg_begin()->hasStructRetAttr())) 84 report_fatal_error("Functions with 'no-prototype' attribute should " 85 "not have params: " + 86 F.getName()); 87 } 88 89 // Create a function prototype based on the first call site (first bitcast) 90 // that we find. 91 FunctionType *NewType = nullptr; 92 for (Use &U : F.uses()) { 93 LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n"); 94 LLVM_DEBUG(dbgs() << *U.getUser() << "\n"); 95 if (auto *BC = dyn_cast<BitCastOperator>(U.getUser())) { 96 if (auto *DestType = dyn_cast<FunctionType>( 97 BC->getDestTy()->getPointerElementType())) { 98 if (!NewType) { 99 // Create a new function with the correct type 100 NewType = DestType; 101 LLVM_DEBUG(dbgs() << "found function type: " << *NewType << "\n"); 102 } else if (NewType != DestType) { 103 errs() << "warning: prototype-less function used with " 104 "conflicting signatures: " 105 << F.getName() << "\n"; 106 LLVM_DEBUG(dbgs() << " " << *DestType << "\n"); 107 LLVM_DEBUG(dbgs() << " "<< *NewType << "\n"); 108 } 109 } 110 } 111 } 112 113 if (!NewType) { 114 LLVM_DEBUG( 115 dbgs() << "could not derive a function prototype from usage: " + 116 F.getName() + "\n"); 117 // We could not derive a type for this function. In this case strip 118 // the isVarArg and make it a simple zero-arg function. This has more 119 // chance of being correct. The current signature of (...) is illegal in 120 // C since it doesn't have any arguments before the "...", we this at 121 // least makes it possible for this symbol to be resolved by the linker. 122 NewType = FunctionType::get(F.getFunctionType()->getReturnType(), false); 123 } 124 125 Function *NewF = 126 Function::Create(NewType, F.getLinkage(), F.getName() + ".fixed_sig"); 127 NewF->setAttributes(F.getAttributes()); 128 NewF->removeFnAttr("no-prototype"); 129 Replacements.emplace_back(&F, NewF); 130 } 131 132 for (auto &Pair : Replacements) { 133 Function *OldF = Pair.first; 134 Function *NewF = Pair.second; 135 std::string Name = OldF->getName(); 136 M.getFunctionList().push_back(NewF); 137 OldF->replaceAllUsesWith( 138 ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF, OldF->getType())); 139 OldF->eraseFromParent(); 140 NewF->setName(Name); 141 } 142 143 return !Replacements.empty(); 144} 145