File: WebAssemblyAddMissingPrototypes.cpp

package info (click to toggle)
llvm-toolchain-7 1%3A7.0.1-8
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 733,456 kB
  • sloc: cpp: 3,776,651; ansic: 633,271; asm: 350,301; python: 142,716; objc: 107,612; sh: 22,626; lisp: 11,056; perl: 7,999; pascal: 6,742; ml: 5,537; awk: 3,536; makefile: 2,557; cs: 2,027; xml: 841; ruby: 156
file content (144 lines) | stat: -rw-r--r-- 4,944 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Add prototypes to prototypes-less functions.
///
/// WebAssembly has strict function prototype checking so we need functions
/// declarations to match the call sites.  Clang treats prototype-less functions
/// as varargs (foo(...)) which happens to work on existing platforms but
/// doesn't under WebAssembly.  This pass will find all the call sites of each
/// prototype-less function, ensure they agree, and then set the signature
/// on the function declaration accordingly.
///
//===----------------------------------------------------------------------===//

#include "WebAssembly.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
using namespace llvm;

#define DEBUG_TYPE "wasm-add-missing-prototypes"

namespace {
class WebAssemblyAddMissingPrototypes final : public ModulePass {
  StringRef getPassName() const override {
    return "Add prototypes to prototypes-less functions";
  }

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.setPreservesCFG();
    ModulePass::getAnalysisUsage(AU);
  }

  bool runOnModule(Module &M) override;

public:
  static char ID;
  WebAssemblyAddMissingPrototypes() : ModulePass(ID) {}
};
} // End anonymous namespace

char WebAssemblyAddMissingPrototypes::ID = 0;
INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE,
                "Add prototypes to prototypes-less functions", false, false)

ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
  return new WebAssemblyAddMissingPrototypes();
}

bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
  LLVM_DEBUG(dbgs() << "runnning AddMissingPrototypes\n");

  std::vector<std::pair<Function*, Function*>> Replacements;

  // Find all the prototype-less function declarations
  for (Function &F : M) {
    if (!F.isDeclaration() || !F.hasFnAttribute("no-prototype"))
      continue;

    LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName() << "\n");

    // When clang emits prototype-less C functions it uses (...), i.e. varargs
    // function that take no arguments (have no sentinel).  When we see a
    // no-prototype attribute we expect the function have these properties.
    if (!F.isVarArg())
      report_fatal_error(
          "Functions with 'no-prototype' attribute must take varargs: " +
          F.getName());
    if (F.getFunctionType()->getNumParams() != 0)
      report_fatal_error(
          "Functions with 'no-prototype' attribute should not have params: " +
          F.getName());


    // Create a function prototype based on the first call site (first bitcast)
    // that we find.
    FunctionType *NewType = nullptr;
    Function* NewF = nullptr;
    for (Use &U : F.uses()) {
      LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n");
      if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) {
        FunctionType *DestType =
            cast<FunctionType>(BC->getDestTy()->getPointerElementType());

        // Create a new function with the correct type
        NewType = DestType;
        NewF = Function::Create(NewType, F.getLinkage(), F.getName());
        NewF->setAttributes(F.getAttributes());
        NewF->removeFnAttr("no-prototype");
        break;
      }
    }

    if (!NewType) {
      LLVM_DEBUG(
          dbgs() << "could not derive a function prototype from usage: " +
                        F.getName() + "\n");
      continue;
    }

    for (Use &U : F.uses()) {
      if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) {
        FunctionType *DestType =
            cast<FunctionType>(BC->getDestTy()->getPointerElementType());
        if (NewType != DestType) {
          report_fatal_error(
              "Prototypeless function used with conflicting signatures: " +
              F.getName());
        }
        BC->replaceAllUsesWith(NewF);
        Replacements.emplace_back(&F, NewF);
      } else {
        dbgs() << *U.getUser()->getType() << "\n";
#ifndef NDEBUG
        U.getUser()->dump();
#endif
        report_fatal_error(
            "unexpected use of prototypeless function: " + F.getName() + "\n");
      }
    }
  }

  // Finally replace the old function declarations with the new ones
  for (auto &Pair : Replacements) {
    Function* Old = Pair.first;
    Function* New = Pair.second;
    Old->eraseFromParent();
    M.getFunctionList().push_back(New);
  }

  return !Replacements.empty();
}