Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 048eb19

Browse files
authored
create a legalizer stub for imported methods with float32 params/return values that go in tables (#168)
* create a legalizer stub for imported methods with float32 params/return values that go in tables, as due to asm.js overloading of float/double, we can't legalize them later - we don't know the type of the indirect calls
1 parent c3984b0 commit 048eb19

File tree

1 file changed

+74
-2
lines changed

1 file changed

+74
-2
lines changed

lib/Target/JSBackend/JSBackend.cpp

+74-2
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ namespace {
233233
BlockAddressMap BlockAddresses;
234234
std::map<std::string, AsmConstInfo> AsmConsts; // code => { index, list of seen sigs }
235235
NameSet FuncRelocatableExterns; // which externals are accessed in this function; we load them once at the beginning (avoids a potential call in a heap access, and might be faster)
236+
std::vector<std::string> ExtraFunctions;
236237
std::set<const Function*> DeclaresNeedingTypeDeclarations; // list of declared funcs whose type we must declare asm.js-style with a usage, as they may not have another usage
237238

238239
struct {
@@ -409,6 +410,55 @@ namespace {
409410
while (Table.size() < MinSize) Table.push_back("0");
410411
return Table;
411412
}
413+
bool usesFloat32(FunctionType* F) {
414+
if (F->getReturnType()->isFloatTy()) return true;
415+
for (FunctionType::param_iterator AI = F->param_begin(),
416+
AE = F->param_end(); AI != AE; ++AI) {
417+
if ((*AI)->isFloatTy()) return true;
418+
}
419+
return false;
420+
}
421+
// create a lettered argument name (a, b, c, etc.)
422+
std::string getArgLetter(int Index) {
423+
std::string Ret = "";
424+
while (1) {
425+
auto Curr = Index % 26;
426+
Ret += char('a' + Curr);
427+
Index = Index / 26;
428+
if (Index == 0) return Ret;
429+
}
430+
}
431+
std::string makeFloat32Legalizer(const Function *F) {
432+
auto* FT = F->getFunctionType();
433+
const std::string& Name = getJSName(F);
434+
std::string LegalName = Name + "$legalf32";
435+
std::string LegalFunc = "function " + LegalName + "(";
436+
std::string Declares = "";
437+
std::string Call = Name + "(";
438+
int Index = 0;
439+
for (FunctionType::param_iterator AI = FT->param_begin(),
440+
AE = FT->param_end(); AI != AE; ++AI) {
441+
if (Index > 0) {
442+
LegalFunc += ", ";
443+
Declares += " ";
444+
Call += ", ";
445+
}
446+
auto Arg = getArgLetter(Index);
447+
LegalFunc += Arg;
448+
Declares += Arg + " = " + getCast(Arg, *AI) + ';';
449+
Call += getCast(Arg, *AI, ASM_NONSPECIFIC | ASM_FFI_OUT);
450+
Index++;
451+
}
452+
LegalFunc += ") {\n ";
453+
LegalFunc += Declares + "\n ";
454+
Call += ")";
455+
if (!FT->getReturnType()->isVoidTy()) {
456+
Call = "return " + getCast(Call, FT->getReturnType(), ASM_FFI_IN);
457+
}
458+
LegalFunc += Call + ";\n}";
459+
ExtraFunctions.push_back(LegalFunc);
460+
return LegalName;
461+
}
412462
unsigned getFunctionIndex(const Function *F) {
413463
const std::string &Name = getJSName(F);
414464
if (IndexedFunctions.find(Name) != IndexedFunctions.end()) return IndexedFunctions[Name];
@@ -423,7 +473,26 @@ namespace {
423473
unsigned Alignment = 1;
424474
while (Table.size() % Alignment) Table.push_back("0");
425475
unsigned Index = Table.size();
426-
Table.push_back(Name);
476+
// add the name to the table. normally we can just add the function itself,
477+
// however, that may not be valid in wasm. consider an imported function with an
478+
// f32 parameter - due to asm.js ffi rules, we must send it f64s. So its
479+
// uses will appear to use f64s, but when called through the function table,
480+
// it must use an f32 for wasm correctness. so we must have an import with
481+
// f64, and put a thunk in the table which accepts f32 and redirects to the
482+
// import. Note that this cannot be done in a later stage, like binaryen's
483+
// legalization, as f32/f64 asm.js overloading can mask it. Note that this
484+
// isn't an issue for i64s even though they are illegal, precisely because
485+
// f32/f64 overloading is possible but i64s don't overload in asm.js with
486+
// anything.
487+
// TODO: if there are no uses of F (aside from being in the table) then
488+
// we don't need this, as we'll add a use in
489+
// DeclaresNeedingTypeDeclarations which will have the proper type,
490+
// and nothing will contradict it/overload it.
491+
if (WebAssembly && F->isDeclaration() && usesFloat32(F->getFunctionType())) {
492+
Table.push_back(makeFloat32Legalizer(F));
493+
} else {
494+
Table.push_back(Name);
495+
}
427496
IndexedFunctions[Name] = Index;
428497
if (NoAliasingFunctionPointers) {
429498
NextFunctionIndex = Index+1;
@@ -435,7 +504,7 @@ namespace {
435504
(this->*(CH->second))(NULL, Name, -1);
436505
}
437506

438-
// and in asm.js, types are inferred from use. so if we have a method that *only* appears in a table, it therefore has no use,
507+
// in asm.js, types are inferred from use. so if we have a method that *only* appears in a table, it therefore has no use,
439508
// and we are in trouble; emit a fake dce-able use for it.
440509
if (WebAssembly) {
441510
if (F->isDeclaration()) {
@@ -3459,6 +3528,9 @@ void JSWriter::printModuleBody() {
34593528
}
34603529
Out << "}\n";
34613530
}
3531+
for (auto& Name : ExtraFunctions) {
3532+
Out << Name << '\n';
3533+
}
34623534
}
34633535
Out << "// EMSCRIPTEN_END_FUNCTIONS\n\n";
34643536

0 commit comments

Comments
 (0)