namespace {
class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
- static const char *ThrewGVName;
- static const char *ThrewValueGVName;
- static const char *TempRet0GVName;
static const char *ResumeFName;
static const char *EHTypeIDFName;
- static const char *SetThrewFName;
- static const char *SetTempRet0FName;
static const char *EmLongjmpFName;
static const char *EmLongjmpJmpbufFName;
static const char *SaveSetjmpFName;
};
} // End anonymous namespace
-const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewGVName = "__THREW__";
-const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewValueGVName = "__threwValue";
-const char *WebAssemblyLowerEmscriptenEHSjLj::TempRet0GVName = "__tempRet0";
const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException";
const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName =
"llvm_eh_typeid_for";
-const char *WebAssemblyLowerEmscriptenEHSjLj::SetThrewFName = "setThrew";
-const char *WebAssemblyLowerEmscriptenEHSjLj::SetTempRet0FName = "setTempRet0";
const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpFName =
"emscripten_longjmp";
const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpJmpbufFName =
return true;
}
-// Returns an available name for a global value.
-// If the proposed name already exists in the module, adds '_' at the end of
-// the name until the name is available.
-static inline std::string createGlobalValueName(const Module &M,
- const std::string &Propose) {
- std::string Name = Propose;
- while (M.getNamedGlobal(Name))
- Name += "_";
- return Name;
+static GlobalVariable *createGlobalVariableI32(Module &M, IRBuilder<> &IRB,
+ const char *Name) {
+ if (M.getNamedGlobal(Name))
+ report_fatal_error(Twine("variable name is reserved: ") + Name);
+
+ return new GlobalVariable(M, IRB.getInt32Ty(), false,
+ GlobalValue::WeakODRLinkage, IRB.getInt32(0), Name);
}
// Simple function name mangler.
LLVMContext &C = M.getContext();
IRBuilder<> IRB(C);
- assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists");
+ if (M.getNamedGlobal("setThrew"))
+ report_fatal_error("setThrew already exists");
+
Type *Params[] = {IRB.getInt32Ty(), IRB.getInt32Ty()};
FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false);
Function *F =
- Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M);
+ Function::Create(FTy, GlobalValue::WeakODRLinkage, "setThrew", &M);
Argument *Arg1 = &*(F->arg_begin());
Argument *Arg2 = &*std::next(F->arg_begin());
Arg1->setName("threw");
LLVMContext &C = M.getContext();
IRBuilder<> IRB(C);
- assert(!M.getNamedGlobal(SetTempRet0FName) && "setTempRet0 already exists");
+ if (M.getNamedGlobal("setTempRet0"))
+ report_fatal_error("setTempRet0 already exists");
Type *Params[] = {IRB.getInt32Ty()};
FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false);
Function *F =
- Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M);
+ Function::Create(FTy, GlobalValue::WeakODRLinkage, "setTempRet0", &M);
F->arg_begin()->setName("value");
BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
IRB.SetInsertPoint(EntryBB);
// Create global variables __THREW__, threwValue, and __tempRet0, which are
// used in common for both exception handling and setjmp/longjmp handling
- ThrewGV = new GlobalVariable(M, IRB.getInt32Ty(), false,
- GlobalValue::ExternalLinkage, IRB.getInt32(0),
- createGlobalValueName(M, ThrewGVName));
- ThrewValueGV = new GlobalVariable(
- M, IRB.getInt32Ty(), false, GlobalValue::ExternalLinkage, IRB.getInt32(0),
- createGlobalValueName(M, ThrewValueGVName));
- TempRet0GV = new GlobalVariable(M, IRB.getInt32Ty(), false,
- GlobalValue::ExternalLinkage, IRB.getInt32(0),
- createGlobalValueName(M, TempRet0GVName));
+ ThrewGV = createGlobalVariableI32(M, IRB, "__THREW__");
+ ThrewValueGV = createGlobalVariableI32(M, IRB, "__threwValue");
+ TempRet0GV = createGlobalVariableI32(M, IRB, "__tempRet0");
bool Changed = false;
if (DoSjLj) {
Changed = true; // We have setjmp or longjmp somewhere
- Function *MallocF = M.getFunction("malloc");
- Function *FreeF = M.getFunction("free");
- if (!MallocF || !FreeF)
- report_fatal_error(
- "malloc and free must be linked into the module if setjmp is used");
-
// Register saveSetjmp function
FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
@_ZTIi = external constant i8*
@_ZTIc = external constant i8*
-; CHECK-DAG: @[[__THREW__:__THREW__.*]] = global i32 0
-; CHECK-DAG: @[[THREWVALUE:__threwValue.*]] = global i32 0
-; CHECK-DAG: @[[TEMPRET0:__tempRet0.*]] = global i32 0
+; CHECK-DAG: __THREW__ = weak_odr global i32 0
+; CHECK-DAG: __threwValue = weak_odr global i32 0
+; CHECK-DAG: __tempRet0 = weak_odr global i32 0
; Test invoke instruction with clauses (try-catch block)
define void @clause() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
invoke void @foo(i32 3)
to label %invoke.cont unwind label %lpad
; CHECK: entry:
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3)
-; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]]
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @__THREW__
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__VAL]], 1
; CHECK-NEXT: br i1 %cmp, label %lpad, label %invoke.cont
; CHECK: lpad:
; CHECK-NEXT: %[[FMC:.*]] = call i8* @__cxa_find_matching_catch_4(i8* bitcast (i8** @_ZTIi to i8*), i8* null)
; CHECK-NEXT: %[[IVI1:.*]] = insertvalue { i8*, i32 } undef, i8* %[[FMC]], 0
-; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @[[TEMPRET0]]
+; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @__tempRet0
; CHECK-NEXT: %[[IVI2:.*]] = insertvalue { i8*, i32 } %[[IVI1]], i32 %[[TEMPRET0_VAL]], 1
; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 0
; CHECK-NEXT: %[[CDR:.*]] = extractvalue { i8*, i32 } %[[IVI2]], 1
invoke void @foo(i32 3)
to label %invoke.cont unwind label %lpad
; CHECK: entry:
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3)
-; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]]
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @__THREW__
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__VAL]], 1
; CHECK-NEXT: br i1 %cmp, label %lpad, label %invoke.cont
; CHECK: lpad:
; CHECK-NEXT: %[[FMC:.*]] = call i8* @__cxa_find_matching_catch_4(i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTIc to i8*))
; CHECK-NEXT: %[[IVI1:.*]] = insertvalue { i8*, i32 } undef, i8* %[[FMC]], 0
-; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @[[TEMPRET0]]
+; CHECK-NEXT: %[[TEMPRET0_VAL:.*]] = load i32, i32* @__tempRet0
; CHECK-NEXT: %[[IVI2:.*]] = insertvalue { i8*, i32 } %[[IVI1]], i32 %[[TEMPRET0_VAL]], 1
; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 0
; CHECK-NEXT: extractvalue { i8*, i32 } %[[IVI2]], 1
%0 = invoke noalias i8* @bar(i8 signext 1, i8 zeroext 2)
to label %invoke.cont unwind label %lpad
; CHECK: entry:
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: %0 = call noalias i8* @"__invoke_i8*_i8_i8"(i8* (i8, i8)* @bar, i8 signext 1, i8 zeroext 2)
invoke.cont: ; preds = %entry
; CHECK-DAG: declare i8* @__cxa_find_matching_catch_4(i8*, i8*)
; setThrew function creation
-; CHECK-LABEL: define void @setThrew(i32 %threw, i32 %value) {
+; CHECK-LABEL: define weak_odr void @setThrew(i32 %threw, i32 %value) {
; CHECK: entry:
-; CHECK-NEXT: %[[__THREW__]].val = load i32, i32* @[[__THREW__]]
-; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__]].val, 0
+; CHECK-NEXT: %__THREW__.val = load i32, i32* @__THREW__
+; CHECK-NEXT: %cmp = icmp eq i32 %__THREW__.val, 0
; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
; CHECK: if.then:
-; CHECK-NEXT: store i32 %threw, i32* @[[__THREW__]]
-; CHECK-NEXT: store i32 %value, i32* @[[THREWVALUE]]
+; CHECK-NEXT: store i32 %threw, i32* @__THREW__
+; CHECK-NEXT: store i32 %value, i32* @__threwValue
; CHECK-NEXT: br label %if.end
; CHECK: if.end:
; CHECK-NEXT: ret void
; CHECK: }
; setTempRet0 function creation
-; CHECK-LABEL: define void @setTempRet0(i32 %value) {
+; CHECK-LABEL: define weak_odr void @setTempRet0(i32 %value) {
; CHECK: entry:
-; CHECK-NEXT: store i32 %value, i32* @[[TEMPRET0]]
+; CHECK-NEXT: store i32 %value, i32* @__tempRet0
; CHECK-NEXT: ret void
; CHECK: }
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
@global_var = hidden global i32 0, align 4
-; CHECK-DAG: @[[__THREW__:__THREW__.*]] = global i32 0
-; CHECK-DAG: @[[THREWVALUE:__threwValue.*]] = global i32 0
-; CHECK-DAG: @[[TEMPRET0:__tempRet0.*]] = global i32 0
+; CHECK-DAG: __THREW__ = weak_odr global i32 0
+; CHECK-DAG: __threwValue = weak_odr global i32 0
+; CHECK-DAG: __tempRet0 = weak_odr global i32 0
; Test a simple setjmp - longjmp sequence
define hidden void @setjmp_longjmp() {
; CHECK-NEXT: %[[BUF:.*]] = alloca [1 x %struct.__jmp_buf_tag]
; CHECK-NEXT: %[[ARRAYDECAY:.*]] = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %[[BUF]], i32 0, i32 0
; CHECK-NEXT: %[[SETJMP_TABLE1:.*]] = call i32* @saveSetjmp(%struct.__jmp_buf_tag* %[[ARRAYDECAY]], i32 1, i32* %[[SETJMP_TABLE]], i32 %[[SETJMP_TABLE_SIZE]])
-; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @[[TEMPRET0]]
+; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @__tempRet0
; CHECK-NEXT: br label %entry.split
; CHECK: entry.split:
; CHECK-NEXT: phi i32 [ 0, %entry ], [ %[[LONGJMP_RESULT:.*]], %if.end ]
; CHECK-NEXT: %[[ARRAYDECAY1:.*]] = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %[[BUF]], i32 0, i32 0
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: call void @"__invoke_void_%struct.__jmp_buf_tag*_i32"(void (%struct.__jmp_buf_tag*, i32)* @emscripten_longjmp_jmpbuf, %struct.__jmp_buf_tag* %[[ARRAYDECAY1]], i32 1)
-; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]]
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @__THREW__
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: %[[CMP0:.*]] = icmp ne i32 %__THREW__.val, 0
-; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @[[THREWVALUE]]
+; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @__threwValue
; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %[[THREWVALUE_VAL]], 0
; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]]
; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else1
; CHECK: if.end:
; CHECK-NEXT: %[[LABEL_PHI:.*]] = phi i32 [ %[[LABEL:.*]], %if.end2 ], [ -1, %if.else1 ]
-; CHECK-NEXT: %[[LONGJMP_RESULT]] = load i32, i32* @[[TEMPRET0]]
+; CHECK-NEXT: %[[LONGJMP_RESULT]] = load i32, i32* @__tempRet0
; CHECK-NEXT: switch i32 %[[LABEL_PHI]], label %entry.split.split [
; CHECK-NEXT: i32 1, label %entry.split
; CHECK-NEXT: ]
; CHECK-NEXT: unreachable
; CHECK: if.end2:
-; CHECK-NEXT: store i32 %[[THREWVALUE_VAL]], i32* @[[TEMPRET0]]
+; CHECK-NEXT: store i32 %[[THREWVALUE_VAL]], i32* @__tempRet0
; CHECK-NEXT: br label %if.end
}
to label %try.cont unwind label %lpad
; CHECK: entry.split:
-; CHECK: store i32 0, i32* @[[__THREW__]]
+; CHECK: store i32 0, i32* @__THREW__
; CHECK-NEXT: call void @__invoke_void(void ()* @foo)
-; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]]
-; CHECK-NEXT: store i32 0, i32* @[[__THREW__]]
+; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @__THREW__
+; CHECK-NEXT: store i32 0, i32* @__THREW__
; CHECK-NEXT: %[[CMP0:.*]] = icmp ne i32 %[[__THREW__VAL]], 0
-; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @[[THREWVALUE]]
+; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @__threwValue
; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %[[THREWVALUE_VAL]], 0
; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]]
; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else1
; CHECK: if.then:
; CHECK: %[[VAR0:.*]] = load i32, i32* @global_var, align 4
; CHECK: %[[SETJMP_TABLE1:.*]] = call i32* @saveSetjmp(
-; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @[[TEMPRET0]]
+; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @__tempRet0
; CHECK: if.then.split:
; CHECK: %[[VAR1:.*]] = phi i32 [ %[[VAR0]], %if.then ], [ %[[VAR2:.*]], %if.end3 ]