#include "abi/js.h"
#include "asm_v_wasm.h"
#include "ir/import-utils.h"
#include "ir/table-utils.h"
#include "pass.h"
#include "shared-constants.h"
#include "support/debug.h"
#define DEBUG_TYPE "emscripten-pic"
namespace wasm {
static Global* ensureGlobalImport(Module* module, Name name, Type type) {
ImportInfo info(*module);
if (auto* g = info.getImportedGlobal(ENV, name)) {
return g;
}
auto import = new Global;
import->name = name;
import->module = ENV;
import->base = name;
import->type = type;
module->addGlobal(import);
return import;
}
static Function*
ensureFunctionImport(Module* module, Name name, Signature sig) {
ImportInfo info(*module);
if (auto* f = info.getImportedFunction(ENV, name)) {
return f;
}
auto import = new Function;
import->name = name;
import->module = ENV;
import->base = name;
import->sig = sig;
module->addFunction(import);
return import;
}
struct EmscriptenPIC : public WalkerPass<PostWalker<EmscriptenPIC>> {
EmscriptenPIC(bool sideModule) : sideModule(sideModule) {}
void visitGlobal(Global* curr) {
if (!curr->imported()) {
return;
}
if (curr->module == "GOT.func") {
gotFuncEntries.push_back(curr);
} else if (curr->module == "GOT.mem") {
gotMemEntries.push_back(curr);
} else {
return;
}
curr->module.clear();
curr->init = Builder(*getModule()).makeConst(int32_t(0));
}
void visitModule(Module* module) {
BYN_TRACE("generateAssignGOTEntriesFunction\n");
if (!gotFuncEntries.size() && !gotMemEntries.size()) {
return;
}
Builder builder(*getModule());
Function* assignFunc = builder.makeFunction(
ASSIGN_GOT_ENTRIES, std::vector<NameType>{}, Type::none, {});
Block* block = builder.makeBlock();
assignFunc->body = block;
bool hasSingleMemorySegment =
module->memory.exists && module->memory.segments.size() == 1;
for (Global* g : gotMemEntries) {
auto base = g->base;
if (hasSingleMemorySegment && !sideModule) {
if (auto* ex = module->getExportOrNull(base)) {
if (ex->kind == ExternalKind::Global) {
auto* relativeBase = ExpressionManipulator::copy(
module->memory.segments[0].offset, *module);
auto* offset = builder.makeGlobalGet(
ex->value, module->getGlobal(ex->value)->type);
auto* add = builder.makeBinary(AddInt32, relativeBase, offset);
GlobalSet* globalSet = builder.makeGlobalSet(g->name, add);
block->list.push_back(globalSet);
continue;
}
}
}
Name getter(std::string("g$") + base.c_str());
ensureFunctionImport(module, getter, Signature(Type::none, Type::i32));
Expression* call = builder.makeCall(getter, {}, Type::i32);
GlobalSet* globalSet = builder.makeGlobalSet(g->name, call);
block->list.push_back(globalSet);
}
ImportInfo importInfo(*module);
Global* tableBase = nullptr;
for (Global* g : gotFuncEntries) {
auto* ex = module->getExportOrNull(g->base);
if (ex && !sideModule) {
assert(ex->kind == ExternalKind::Function);
auto* f = module->getFunction(ex->value);
if (f->imported()) {
Fatal() << "GOT.func entry is both imported and exported: "
<< g->base;
}
if (!tableBase) {
tableBase = ensureGlobalImport(module, TABLE_BASE, Type::i32);
}
if (!module->table.exists) {
module->table.exists = true;
}
if (module->table.segments.empty()) {
module->table.segments.resize(1);
module->table.segments[0].offset =
builder.makeGlobalGet(tableBase->name, Type::i32);
}
auto tableIndex =
TableUtils::getOrAppend(module->table, f->name, *module);
auto* c = LiteralUtils::makeFromInt32(tableIndex, Type::i32, *module);
auto* getBase = builder.makeGlobalGet(tableBase->name, Type::i32);
auto* add = builder.makeBinary(AddInt32, getBase, c);
auto* globalSet = builder.makeGlobalSet(g->name, add);
block->list.push_back(globalSet);
continue;
}
auto* f = importInfo.getImportedFunction(ENV, g->base);
if (!f) {
if (!ex) {
Fatal() << "GOT.func entry with no import/export: " << g->base;
}
f = module->getFunction(ex->value);
}
Name getter(
(std::string("fp$") + g->base.c_str() + std::string("$") + getSig(f))
.c_str());
ensureFunctionImport(module, getter, Signature(Type::none, Type::i32));
auto* call = builder.makeCall(getter, {}, Type::i32);
auto* globalSet = builder.makeGlobalSet(g->name, call);
block->list.push_back(globalSet);
}
module->addFunction(assignFunc);
}
std::vector<Global*> gotFuncEntries;
std::vector<Global*> gotMemEntries;
bool sideModule;
};
Pass* createEmscriptenPICPass() { return new EmscriptenPIC(true); }
Pass* createEmscriptenPICMainModulePass() { return new EmscriptenPIC(false); }
}