LLVM: lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp Source File (original) (raw)
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
273#include "llvm/IR/IntrinsicsWebAssembly.h"
280#include
281
282using namespace llvm;
283
284#define DEBUG_TYPE "wasm-lower-em-ehsjlj"
285
288 cl::desc("The list of function names in which Emscripten-style "
289 "exception handling is enabled (see emscripten "
290 "EMSCRIPTEN_CATCHING_ALLOWED options)"),
292
293namespace {
294class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
295 bool EnableEmEH;
296 bool EnableEmSjLj;
297 bool EnableWasmSjLj;
298 bool DoSjLj;
299
300 GlobalVariable *ThrewGV = nullptr;
301 GlobalVariable *ThrewValueGV = nullptr;
302 Function *GetTempRet0F = nullptr;
303 Function *SetTempRet0F = nullptr;
304 Function *ResumeF = nullptr;
305 Function *EHTypeIDF = nullptr;
306 Function *EmLongjmpF = nullptr;
307 Function *WasmSetjmpF = nullptr;
308 Function *WasmSetjmpTestF = nullptr;
309 Function *WasmLongjmpF = nullptr;
310 Function *CatchF = nullptr;
311
312
313 Type *LongjmpArgsTy = nullptr;
314
315
316
318
320
321 std::set<std::string, std::less<>> EHAllowlistSet;
322
324
326 return "WebAssembly Lower Emscripten Exceptions";
327 }
328
330 bool runEHOnFunction(Function &F);
331 bool runSjLjOnFunction(Function &F);
332 void handleLongjmpableCallsForEmscriptenSjLj(
335 void
336 handleLongjmpableCallsForWasmSjLj(Function &F,
339 Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
340
343 Value *FunctionInvocationId, Value *&Label,
345 PHINode *&CallEmLongjmpBBThrewPHI,
346 PHINode *&CallEmLongjmpBBThrewValuePHI,
349
350 bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
351 bool supportsException(const Function *F) const {
352 return EnableEmEH &&
353 (areAllExceptionsAllowed() || EHAllowlistSet.count(F->getName()));
354 }
356
358
359public:
360 static char ID;
361
362 WebAssemblyLowerEmscriptenEHSjLj()
366 assert(!(EnableEmSjLj && EnableWasmSjLj) &&
367 "Two SjLj modes cannot be turned on at the same time");
368 assert(!(EnableEmEH && EnableWasmSjLj) &&
369 "Wasm SjLj should be only used with Wasm EH");
371 }
373
376 }
377};
378}
379
380char WebAssemblyLowerEmscriptenEHSjLj::ID = 0;
382 "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
383 false, false)
384
386 return new WebAssemblyLowerEmscriptenEHSjLj();
387}
388
390 if (const auto *F = dyn_cast(V)) {
391
392 if (F->isIntrinsic())
393 return false;
395
396 if (Name == "setjmp" || Name == "longjmp" || Name == "emscripten_longjmp")
397 return false;
398 return ->doesNotThrow();
399 }
400
401 return true;
402}
403
404
405
406
409 const char *Name) {
410 auto *GV = dyn_cast(M.getOrInsertGlobal(Name, Ty));
411 if (!GV)
413
414
415
416
417
419 return GV;
420}
421
422
423
424
425
426
428 std::string Sig;
430 OS << *FTy->getReturnType();
431 for (Type *ParamTy : FTy->params())
432 OS << "_" << *ParamTy;
433 if (FTy->isVarArg())
434 OS << "_...";
435 Sig = OS.str();
437
438
439 std::replace(Sig.begin(), Sig.end(), ',', '.');
440 return Sig;
441}
442
446
447
448 if (->hasFnAttribute("wasm-import-module")) {
450 B.addAttribute("wasm-import-module", "env");
452 }
453 if (->hasFnAttribute("wasm-import-name")) {
455 B.addAttribute("wasm-import-name", F->getName());
457 }
458 return F;
459}
460
461
462
465 return IRB.getIntNTy(M->getDataLayout().getPointerSizeInBits());
466}
467
468
469
470
472 return PointerType::getUnqual(M->getContext());
473}
474
475
476
477
480 return IRB.getIntN(M->getDataLayout().getPointerSizeInBits(), C);
481}
482
483
484
485
486
487
489WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
490 unsigned NumClauses) {
491 if (FindMatchingCatches.count(NumClauses))
492 return FindMatchingCatches[NumClauses];
493 PointerType *Int8PtrTy = PointerType::getUnqual(M.getContext());
495 FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
497 FTy, "__cxa_find_matching_catch_" + Twine(NumClauses + 2), &M);
498 FindMatchingCatches[NumClauses] = F;
499 return F;
500}
501
502
503
504
505
506
507
508
509Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) {
512
514 IRB.SetInsertPoint(CI);
515
516
517
519
520
522
523
526 CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args);
530
531
532
535
536
538
539 for (unsigned I = 0, E = CI->arg_size(); I < E; ++I)
541
543 if (auto Args = FnAttrs.getAllocSizeArgs()) {
544
545
546 auto [SizeArg, NEltArg] = *Args;
547 SizeArg += 1;
548 if (NEltArg)
549 NEltArg = *NEltArg + 1;
550 FnAttrs.addAllocSizeAttr(SizeArg, NEltArg);
551 }
552
553
554 FnAttrs.removeAttribute(Attribute::NoReturn);
555
556
560
562
563
564
568 return Threw;
569}
570
571
572Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallBase *CI) {
576
578 auto It = InvokeWrappers.find(Sig);
579 if (It != InvokeWrappers.end())
580 return It->second;
581
582
584
585 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
586
587 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
588 CalleeFTy->isVarArg());
590 InvokeWrappers[Sig] = F;
591 return F;
592}
593
595 if (auto *CalleeF = dyn_cast(Callee))
596 if (CalleeF->isIntrinsic())
597 return false;
598
599
600
601
602
603 if (isa(Callee))
604 return false;
605 StringRef CalleeName = Callee->getName();
606
607
608
609
610
611 if (CalleeName == "setjmp" || CalleeName == "malloc" || CalleeName == "free")
612 return false;
613
614
615 if (CalleeName == "__resumeException" || CalleeName == "llvm_eh_typeid_for" ||
616 CalleeName == "__wasm_setjmp" || CalleeName == "__wasm_setjmp_test" ||
617 CalleeName == "getTempRet0" || CalleeName == "setTempRet0")
618 return false;
619
620
621 if (Callee->getName().starts_with("__cxa_find_matching_catch_"))
622 return false;
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659 if (CalleeName == "__cxa_end_catch")
661 if (CalleeName == "__cxa_begin_catch" ||
662 CalleeName == "__cxa_allocate_exception" || CalleeName == "__cxa_throw" ||
663 CalleeName == "__clang_call_terminate")
664 return false;
665
666
667
668 if (CalleeName == "_ZSt9terminatev")
669 return false;
670
671
672 return true;
673}
674
676 StringRef CalleeName = Callee->getName();
677
678 return CalleeName == "emscripten_asm_const_int" ||
679 CalleeName == "emscripten_asm_const_double" ||
680 CalleeName == "emscripten_asm_const_int_sync_on_main_thread" ||
681 CalleeName == "emscripten_asm_const_double_sync_on_main_thread" ||
682 CalleeName == "emscripten_asm_const_async_on_main_thread";
683}
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
704 PHINode *&CallEmLongjmpBBThrewPHI, PHINode *&CallEmLongjmpBBThrewValuePHI,
710 IRB.SetCurrentDebugLocation(DL);
711
712
713 IRB.SetInsertPoint(BB);
718 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
719 ThrewValueGV->getName() + ".val");
720 Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0));
721 Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1");
722 IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1);
723
724
725 if (!CallEmLongjmpBB) {
726
728 IRB.SetInsertPoint(CallEmLongjmpBB);
729 CallEmLongjmpBBThrewPHI = IRB.CreatePHI(getAddrIntType(M), 4, "threw.phi");
730 CallEmLongjmpBBThrewValuePHI =
731 IRB.CreatePHI(IRB.getInt32Ty(), 4, "threwvalue.phi");
732 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
733 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
734 IRB.CreateCall(EmLongjmpF,
735 {CallEmLongjmpBBThrewPHI, CallEmLongjmpBBThrewValuePHI});
736 IRB.CreateUnreachable();
737 } else {
738 CallEmLongjmpBBThrewPHI->addIncoming(Threw, ThenBB1);
739 CallEmLongjmpBBThrewValuePHI->addIncoming(ThrewValue, ThenBB1);
740 }
741
742
743
744 IRB.SetInsertPoint(ThenBB1);
746 Value *ThrewPtr =
748 Value *ThenLabel = IRB.CreateCall(WasmSetjmpTestF,
749 {ThrewPtr, FunctionInvocationId}, "label");
750 Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0));
751 IRB.CreateCondBr(Cmp2, CallEmLongjmpBB, EndBB2);
752
753
754 IRB.SetInsertPoint(EndBB2);
755 IRB.CreateCall(SetTempRet0F, ThrewValue);
756 IRB.CreateBr(EndBB1);
757
758 IRB.SetInsertPoint(ElseBB1);
759 IRB.CreateBr(EndBB1);
760
761
762 IRB.SetInsertPoint(EndBB1);
763 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
764 LabelPHI->addIncoming(ThenLabel, EndBB2);
765
766 LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1);
767
768
769 Label = LabelPHI;
770 EndBB = EndBB1;
771 LongjmpResult = IRB.CreateCall(GetTempRet0F, {}, "longjmp_result");
772}
773
774void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
775 DominatorTree &DT = getAnalysis(F).getDomTree();
777
781 if (I.getType()->isVoidTy())
782 continue;
783 unsigned VarID = SSA.AddVariable(I.getName(), I.getType());
784
785
786 if (auto *II = dyn_cast(&I))
787 SSA.AddAvailableValue(VarID, II->getNormalDest(), II);
788 else
789 SSA.AddAvailableValue(VarID, &BB, &I);
790 for (auto &U : I.uses()) {
791 auto *User = cast(U.getUser());
792 if (auto *UserPN = dyn_cast(User))
793 if (UserPN->getIncomingBlock(U) == &BB)
794 continue;
796 continue;
798 }
799 }
800 }
801 SSA.RewriteAllUses(&DT);
802}
803
804
805
806
807
808
809
810
811
812void WebAssemblyLowerEmscriptenEHSjLj::replaceLongjmpWith(Function *LongjmpF,
814 assert(NewF == EmLongjmpF || NewF == WasmLongjmpF);
819
820
821
822 for (User *U : LongjmpF->users()) {
823 auto *CI = dyn_cast(U);
825 IRB.SetInsertPoint(CI);
826 Value *Env = nullptr;
827 if (NewF == EmLongjmpF)
828 Env =
830 else
831 Env = IRB.CreateBitCast(CI->getArgOperand(0), IRB.getPtrTy(), "env");
832 IRB.CreateCall(NewF, {Env, CI->getArgOperand(1)});
834 }
835 }
836 for (auto *I : ToErase)
837 I->eraseFromParent();
838
839
840
841 if (!LongjmpF->uses().empty()) {
842 Value *NewLongjmp =
843 IRB.CreateBitCast(NewF, LongjmpF->getType(), "longjmp.cast");
845 }
846}
847
849 for (const auto &BB : *F)
850 for (const auto &I : BB)
851 if (const auto *CB = dyn_cast(&I))
852 if (canLongjmp(CB->getCalledOperand()))
853 return true;
854 return false;
855}
856
857
858
859
860
862 Module &M = *F->getParent();
864 Function *SetjmpF = M.getFunction("setjmp");
866
868 auto *CB = cast(U);
871 continue;
873
874 if (auto *II = dyn_cast(CB))
876 else
877 CI = cast(CB);
880 }
881 for (auto *I : ToErase)
882 I->eraseFromParent();
883}
884
885bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
886 LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
887
890
891 Function *SetjmpF = M.getFunction("setjmp");
892 Function *LongjmpF = M.getFunction("longjmp");
893
894
895
896
897 Function *SetjmpF2 = M.getFunction("_setjmp");
898 Function *LongjmpF2 = M.getFunction("_longjmp");
899 if (SetjmpF2) {
900 if (SetjmpF) {
902 report_fatal_error("setjmp and _setjmp have different function types");
903 } else {
906 }
908 }
909 if (LongjmpF2) {
910 if (LongjmpF) {
913 "longjmp and _longjmp have different function types");
914 } else {
917 }
919 }
920
921 auto *TPC = getAnalysisIfAvailable();
922 assert(TPC && "Expected a TargetPassConfig");
924
925
926
927
929 ThrewValueGV = getGlobalVariable(M, IRB.getInt32Ty(), TM, "__threwValue");
931 FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
933 FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
934 "setTempRet0", &M);
937
938 bool Changed = false;
939
940
941 if (EnableEmEH) {
942
944 FunctionType::get(IRB.getVoidTy(), IRB.getPtrTy(), false);
946 ResumeF->addFnAttr(Attribute::NoReturn);
947
948
950 FunctionType::get(IRB.getInt32Ty(), IRB.getPtrTy(), false);
952 }
953
954
955
957
958 if ((EnableEmSjLj || EnableWasmSjLj) && SetjmpF) {
959
960 for (User *U : SetjmpF->users()) {
961 if (auto *CB = dyn_cast(U)) {
962 auto *UserF = CB->getFunction();
963
964
965
967 SetjmpUsers.insert(UserF);
968 else
969 SetjmpUsersToNullify.insert(UserF);
970 } else {
971 std::string S;
975 SS.str());
976 }
977 }
978 }
979
980 bool SetjmpUsed = SetjmpF && !SetjmpUsers.empty();
981 bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
982 DoSjLj = (EnableEmSjLj | EnableWasmSjLj) && (SetjmpUsed || LongjmpUsed);
983
984
985 if (DoSjLj) {
986 assert(EnableEmSjLj || EnableWasmSjLj);
987 if (EnableEmSjLj) {
988
990 IRB.getVoidTy(), {getAddrIntType(&M), IRB.getInt32Ty()}, false);
992 EmLongjmpF->addFnAttr(Attribute::NoReturn);
993 } else {
994 Type *Int8PtrTy = IRB.getPtrTy();
995
997 IRB.getVoidTy(), {Int8PtrTy, IRB.getInt32Ty()}, false);
999 WasmLongjmpF->addFnAttr(Attribute::NoReturn);
1000 }
1001
1002 if (SetjmpF) {
1003 Type *Int8PtrTy = IRB.getPtrTy();
1004 Type *Int32PtrTy = IRB.getPtrTy();
1006
1007
1010 IRB.getVoidTy(), {SetjmpFTy->getParamType(0), Int32Ty, Int32PtrTy},
1011 false);
1013
1014
1015 FTy = FunctionType::get(Int32Ty, {Int32PtrTy, Int32PtrTy}, false);
1017
1018
1019
1021
1023 Int32Ty
1024 );
1025 }
1026 }
1027
1028
1029 if (EnableEmEH) {
1031 if (F.isDeclaration())
1032 continue;
1033 Changed |= runEHOnFunction(F);
1034 }
1035 }
1036
1037
1038 if (DoSjLj) {
1039 Changed = true;
1040 if (LongjmpF)
1041 replaceLongjmpWith(LongjmpF, EnableEmSjLj ? EmLongjmpF : WasmLongjmpF);
1042
1043
1044 if (SetjmpF)
1046 runSjLjOnFunction(*F);
1047 }
1048
1049
1050 if ((EnableEmSjLj || EnableWasmSjLj) && !SetjmpUsersToNullify.empty()) {
1051 Changed = true;
1053 for (Function *F : SetjmpUsersToNullify)
1055 }
1056
1057
1058 for (auto *V : {ThrewGV, ThrewValueGV})
1059 if (V && V->use_empty())
1060 V->eraseFromParent();
1061 for (auto *V : {GetTempRet0F, SetTempRet0F, ResumeF, EHTypeIDF, EmLongjmpF,
1062 WasmSetjmpF, WasmSetjmpTestF, WasmLongjmpF, CatchF})
1063 if (V && V->use_empty())
1064 V->eraseFromParent();
1065
1066 return Changed;
1067}
1068
1069bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
1073 bool Changed = false;
1076
1077
1078 BasicBlock *RethrowLongjmpBB = nullptr;
1079
1080
1081 PHINode *RethrowLongjmpBBThrewPHI = nullptr;
1082
1084 auto *II = dyn_cast(BB.getTerminator());
1085 if ()
1086 continue;
1087 Changed = true;
1088 LandingPads.insert(II->getLandingPadInst());
1089 IRB.SetInsertPoint(II);
1090
1092 bool NeedInvoke = supportsException(&F) && canThrow(Callee);
1093 if (NeedInvoke) {
1094
1095 Value *Threw = wrapInvoke(II);
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 if (DoSjLj && EnableEmSjLj && !SetjmpUsers.count(&F) &&
1120
1121 if (!RethrowLongjmpBB) {
1123 IRB.SetInsertPoint(RethrowLongjmpBB);
1124 RethrowLongjmpBBThrewPHI =
1126 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1127 Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV,
1128 ThrewValueGV->getName() + ".val");
1129 IRB.CreateCall(EmLongjmpF, {RethrowLongjmpBBThrewPHI, ThrewValue});
1130 IRB.CreateUnreachable();
1131 } else {
1132 RethrowLongjmpBBThrewPHI->addIncoming(Threw, &BB);
1133 }
1134
1135 IRB.SetInsertPoint(II);
1137 Value *CmpEqOne =
1138 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1139 Value *CmpEqZero =
1140 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 0), "cmp.eq.zero");
1141 Value *Or = IRB.CreateOr(CmpEqZero, CmpEqOne, "or");
1142 IRB.CreateCondBr(Or, Tail, RethrowLongjmpBB);
1143 IRB.SetInsertPoint(Tail);
1144 BB.replaceSuccessorsPhiUsesWith(&BB, Tail);
1145 }
1146
1147
1149 IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest());
1150
1151 } else {
1152
1153
1155 }
1156 }
1157
1158
1160
1162 auto *RI = dyn_cast(&I);
1163 if (!RI)
1164 continue;
1165 Changed = true;
1166
1167
1168 Value *Input = RI->getValue();
1169 IRB.SetInsertPoint(RI);
1170 Value *Low = IRB.CreateExtractValue(Input, 0, "low");
1171
1172 IRB.CreateCall(ResumeF, {Low});
1173
1174 IRB.CreateUnreachable();
1176 }
1177 }
1178
1179
1182 auto *CI = dyn_cast(&I);
1183 if (!CI)
1184 continue;
1186 if (!Callee)
1187 continue;
1188 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
1189 continue;
1190 Changed = true;
1191
1192 IRB.SetInsertPoint(CI);
1194 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
1197 }
1198 }
1199
1200
1203 if (auto *LPI = dyn_cast(I))
1204 LandingPads.insert(LPI);
1205 }
1206 Changed |= !LandingPads.empty();
1207
1208
1209
1211 IRB.SetInsertPoint(LPI);
1213 for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) {
1215
1216
1217 if (LPI->isCatch(I))
1219 }
1220
1221
1222 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
1223 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
1225 Value *Pair0 = IRB.CreateInsertValue(Poison, FMCI, 0, "pair0");
1226 Value *TempRet0 = IRB.CreateCall(GetTempRet0F, {}, "tempret0");
1227 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
1228
1231 }
1232
1233
1235 I->eraseFromParent();
1236
1237 return Changed;
1238}
1239
1240
1241
1242
1243
1244
1245
1246
1247
1250 assert(InsertBefore);
1256 if (SP)
1257 return DILocation::get(SP->getContext(), SP->getLine(), 1, SP);
1259}
1260
1261bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
1262 assert(EnableEmSjLj || EnableWasmSjLj);
1267
1268
1269
1273
1274 IRB.SetInsertPoint(Entry->getTerminator()->getIterator());
1275
1276
1277
1279 IRB.CreateAlloca(IRB.getInt32Ty(), nullptr, "functionInvocationId");
1280 FunctionInvocationId->setDebugLoc(FirstDL);
1281
1282
1284 Function *SetjmpF = M.getFunction("setjmp");
1286 auto *CB = cast(U);
1288 if (BB->getParent() != &F)
1289 continue;
1291 std::string S;
1293 SS << "In function " + F.getName() +
1294 ": setjmp within a catch clause is not supported in Wasm EH:\n";
1295 SS << *CB;
1297 }
1298
1300
1301 if (auto *II = dyn_cast(CB))
1303 else
1304 CI = cast(CB);
1305
1306
1307
1309
1310
1311
1312 IRB.SetInsertPoint(Tail, Tail->getFirstNonPHIIt());
1313 PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret");
1314
1315
1316 SetjmpRet->addIncoming(IRB.getInt32(0), BB);
1317
1319
1320 SetjmpRetPHIs.push_back(SetjmpRet);
1321
1322
1323
1324
1325 IRB.SetInsertPoint(CI);
1327 FunctionInvocationId};
1328 IRB.CreateCall(WasmSetjmpF, Args);
1330 }
1331
1332
1333 if (EnableEmSjLj)
1334 handleLongjmpableCallsForEmscriptenSjLj(F, FunctionInvocationId,
1335 SetjmpRetPHIs);
1336 else
1337 handleLongjmpableCallsForWasmSjLj(F, FunctionInvocationId, SetjmpRetPHIs);
1338
1339
1341 I->eraseFromParent();
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353 rebuildSSA(F);
1354 return true;
1355}
1356
1357
1358
1359
1360void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj(
1367
1368
1369 BasicBlock *CallEmLongjmpBB = nullptr;
1370
1371
1372 PHINode *CallEmLongjmpBBThrewPHI = nullptr;
1373
1374
1375 PHINode *CallEmLongjmpBBThrewValuePHI = nullptr;
1376
1378
1379
1380
1381
1382 std::vector<BasicBlock *> BBs;
1384 BBs.push_back(&BB);
1385
1386
1387 for (unsigned I = 0; I < BBs.size(); I++) {
1390 if (isa(&I)) {
1391 std::string S;
1393 SS << "In function " << F.getName()
1394 << ": When using Wasm EH with Emscripten SjLj, there is a "
1395 "restriction that `setjmp` function call and exception cannot be "
1396 "used within the same function:\n";
1399 }
1400 auto *CI = dyn_cast(&I);
1401 if (!CI)
1402 continue;
1403
1406 continue;
1408 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1409 F.getName() +
1410 ". Please consider using EM_JS, or move the "
1411 "EM_ASM into another function.",
1412 false);
1413
1414 Value *Threw = nullptr;
1416 if (Callee->getName().starts_with("__invoke_")) {
1417
1418
1419
1420
1421 LoadInst *ThrewLI = nullptr;
1422 StoreInst *ThrewResetSI = nullptr;
1425 if (auto *LI = dyn_cast(I))
1426 if (auto *GV = dyn_cast(LI->getPointerOperand()))
1427 if (GV == ThrewGV) {
1428 Threw = ThrewLI = LI;
1429 break;
1430 }
1431 }
1432
1433
1436 if (auto *SI = dyn_cast(I)) {
1437 if (auto *GV = dyn_cast(SI->getPointerOperand())) {
1438 if (GV == ThrewGV &&
1440 ThrewResetSI = SI;
1441 break;
1442 }
1443 }
1444 }
1445 }
1446 assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke");
1447 assert(ThrewResetSI && "Cannot find __THREW__ store after invoke");
1449
1450 } else {
1451
1452 Threw = wrapInvoke(CI);
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477 if (supportsException(&F) && canThrow(Callee)) {
1478
1479
1480 ToErase.push_back(BB->getTerminator());
1481
1482
1483 if (!RethrowExnBB) {
1485 IRB.SetInsertPoint(RethrowExnBB);
1487 IRB.CreateCall(getFindMatchingCatch(M, 0), {}, "exn");
1488 IRB.CreateCall(ResumeF, {Exn});
1489 IRB.CreateUnreachable();
1490 }
1491
1492 IRB.SetInsertPoint(CI);
1494 Value *CmpEqOne =
1495 IRB.CreateICmpEQ(Threw, getAddrSizeInt(&M, 1), "cmp.eq.one");
1496 IRB.CreateCondBr(CmpEqOne, RethrowExnBB, NormalBB);
1497
1498 IRB.SetInsertPoint(NormalBB);
1499 IRB.CreateBr(Tail);
1500 BB = NormalBB;
1501 }
1502 }
1503
1504
1505
1506
1507 ToErase.push_back(BB->getTerminator());
1508
1509
1510
1511
1513 Value *LongjmpResult = nullptr;
1515 wrapTestSetjmp(BB, CI->getDebugLoc(), Threw, FunctionInvocationId, Label,
1516 LongjmpResult, CallEmLongjmpBB, CallEmLongjmpBBThrewPHI,
1517 CallEmLongjmpBBThrewValuePHI, EndBB);
1518 assert(Label && LongjmpResult && EndBB);
1519
1520
1521 IRB.SetInsertPoint(EndBB);
1522 IRB.SetCurrentDebugLocation(EndBB->back().getDebugLoc());
1524
1525
1526
1527
1528 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1529 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1530 SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB);
1531 }
1532
1533
1534
1536 }
1537 }
1538
1540 I->eraseFromParent();
1541}
1542
1544 for (const User *U : CPI->users())
1545 if (const auto *CRI = dyn_cast(U))
1546 return CRI->getUnwindDest();
1547 return nullptr;
1548}
1549
1550
1551
1552
1553
1554
1555void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj(
1561
1562
1563
1564
1565
1566 if (.hasPersonalityFn()) {
1569 FunctionType::get(IRB.getInt32Ty(), true);
1570 Value *PersF = M.getOrInsertFunction(PersName, PersType).getCallee();
1571 F.setPersonalityFn(
1572 cast(IRB.CreateBitCast(PersF, IRB.getPtrTy())));
1573 }
1574
1575
1578 IRB.SetCurrentDebugLocation(FirstDL);
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1594 cast(Entry->getTerminator())->setSuccessor(0, SetjmpDispatchBB);
1595
1596
1597 BasicBlock *CatchDispatchLongjmpBB =
1599 IRB.SetInsertPoint(CatchDispatchLongjmpBB);
1602
1603
1605 CatchSwitchLongjmp->addHandler(CatchLongjmpBB);
1606 IRB.SetInsertPoint(CatchLongjmpBB);
1607 CatchPadInst *CatchPad = IRB.CreateCatchPad(CatchSwitchLongjmp, {});
1608
1609
1610
1611
1612
1615 Value *EnvField =
1616 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 0, "env_gep");
1617 Value *ValField =
1618 IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 1, "val_gep");
1619
1620 Instruction *Env = IRB.CreateLoad(IRB.getPtrTy(), EnvField, "env");
1621
1622 Instruction *Val = IRB.CreateLoad(IRB.getInt32Ty(), ValField, "val");
1623
1624
1625
1626
1627
1631 Value *Label = IRB.CreateCall(WasmSetjmpTestF, {EnvP, FunctionInvocationId},
1633 Value *Cmp = IRB.CreateICmpEQ(Label, IRB.getInt32(0));
1634 IRB.CreateCondBr(Cmp, ThenBB, EndBB);
1635
1636 IRB.SetInsertPoint(ThenBB);
1637 CallInst *WasmLongjmpCI = IRB.CreateCall(
1638 WasmLongjmpF, {Env, Val}, OperandBundleDef("funclet", CatchPad));
1639 IRB.CreateUnreachable();
1640
1641 IRB.SetInsertPoint(EndBB);
1642
1643 IRB.CreateCatchRet(CatchPad, SetjmpDispatchBB);
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653 IRB.SetInsertPoint(SetjmpDispatchBB);
1654 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label.phi");
1657 SwitchInst *SI = IRB.CreateSwitch(LabelPHI, OrigEntry, SetjmpRetPHIs.size());
1658
1659
1660
1661
1662 for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) {
1663 SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent());
1664 SetjmpRetPHIs[I]->addIncoming(Val, SetjmpDispatchBB);
1665 }
1666
1667
1668
1670 for (auto *BB = &*F.begin(); BB; BB = BB->getNextNode()) {
1671 for (auto &I : *BB) {
1672 auto *CI = dyn_cast(&I);
1673 if (!CI)
1674 continue;
1677 continue;
1679 report_fatal_error("Cannot use EM_ASM* alongside setjmp/longjmp in " +
1680 F.getName() +
1681 ". Please consider using EM_JS, or move the "
1682 "EM_ASM into another function.",
1683 false);
1684
1685
1686
1687 if (CI == WasmLongjmpCI)
1688 continue;
1690 }
1691 }
1692
1694 UnwindDestToNewPreds;
1695 for (auto *CI : LongjmpableCalls) {
1696
1697
1698
1701 CalleeF->removeFnAttr(Attribute::NoUnwind);
1702
1703
1704
1705
1706
1710 Instruction *FromPad = cast(Bundle->Inputs[0]);
1711 while (!UnwindDest) {
1712 if (auto *CPI = dyn_cast(FromPad)) {
1713 UnwindDest = CPI->getCatchSwitch()->getUnwindDest();
1714 break;
1715 }
1716 if (auto *CPI = dyn_cast(FromPad)) {
1717
1718
1719
1720
1721
1723 Value *ParentPad = CPI->getParentPad();
1724 if (isa(ParentPad))
1725 break;
1726 FromPad = cast(ParentPad);
1727 }
1728 }
1729 }
1730 if (!UnwindDest)
1731 UnwindDest = CatchDispatchLongjmpBB;
1732
1733
1734
1735
1736 UnwindDestToNewPreds[UnwindDest].insert(CI->getParent());
1738 }
1739
1741 for (auto &BB : F) {
1742 if (auto *CSI = dyn_cast(BB.getFirstNonPHI())) {
1743 if (CSI != CatchSwitchLongjmp && CSI->unwindsToCaller()) {
1744 IRB.SetInsertPoint(CSI);
1746 auto *NewCSI = IRB.CreateCatchSwitch(CSI->getParentPad(),
1747 CatchDispatchLongjmpBB, 1);
1748 NewCSI->addHandler(*CSI->handler_begin());
1749 NewCSI->takeName(CSI);
1750 CSI->replaceAllUsesWith(NewCSI);
1751 }
1752 }
1753
1754 if (auto *CRI = dyn_cast(BB.getTerminator())) {
1755 if (CRI->unwindsToCaller()) {
1756 IRB.SetInsertPoint(CRI);
1758 IRB.CreateCleanupRet(CRI->getCleanupPad(), CatchDispatchLongjmpBB);
1759 }
1760 }
1761 }
1762
1764 I->eraseFromParent();
1765
1766
1767
1768
1769
1770
1771
1772 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1773 for (PHINode &PN : UnwindDest->phis()) {
1774 for (auto *NewPred : NewPreds) {
1775 assert(PN.getBasicBlockIndex(NewPred) == -1);
1777 }
1778 }
1779 }
1780
1781
1782
1783
1784
1785
1786 for (auto &[UnwindDest, NewPreds] : UnwindDestToNewPreds) {
1787 for (PHINode &PN : UnwindDest->phis()) {
1789 SSA.Initialize(PN.getType(), PN.getName());
1790 for (unsigned Idx = 0, E = PN.getNumIncomingValues(); Idx != E; ++Idx) {
1791 if (NewPreds.contains(PN.getIncomingBlock(Idx)))
1792 continue;
1793 Value *V = PN.getIncomingValue(Idx);
1794 if (auto *II = dyn_cast(V))
1795 SSA.AddAvailableValue(II->getNormalDest(), II);
1796 else if (auto *I = dyn_cast(V))
1797 SSA.AddAvailableValue(I->getParent(), I);
1798 else
1799 SSA.AddAvailableValue(PN.getIncomingBlock(Idx), V);
1800 }
1801 for (auto *NewPred : NewPreds)
1802 PN.setIncomingValueForBlock(NewPred, SSA.GetValueAtEndOfBlock(NewPred));
1803 assert(PN.isComplete());
1804 }
1805 }
1806}
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
Module.h This file contains the declarations for the Module class.
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Target-Independent Code Generator Pass Configuration Options pass.
static void nullifySetjmp(Function *F)
static bool canLongjmp(const Value *Callee)
static cl::list< std::string > EHAllowlist("emscripten-cxx-exceptions-allowed", cl::desc("The list of function names in which Emscripten-style " "exception handling is enabled (see emscripten " "EMSCRIPTEN_CATCHING_ALLOWED options)"), cl::CommaSeparated)
static Type * getAddrPtrType(Module *M)
static std::string getSignature(FunctionType *FTy)
static Type * getAddrIntType(Module *M)
static bool canThrow(const Value *V)
static BasicBlock * getCleanupRetUnwindDest(const CleanupPadInst *CPI)
static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore, DISubprogram *SP)
static bool containsLongjmpableCalls(const Function *F)
static Value * getAddrSizeInt(Module *M, uint64_t C)
static Function * getEmscriptenFunction(FunctionType *Ty, const Twine &Name, Module *M)
static GlobalVariable * getGlobalVariable(Module &M, Type *Ty, WebAssemblyTargetMachine &TM, const char *Name)
static bool isEmAsmCall(const Value *Callee)
This file provides WebAssembly-specific target descriptions.
This file declares the WebAssembly-specific subclass of TargetMachine.
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AttributeSet getFnAttrs() const
The function attributes are returned.
static AttributeList get(LLVMContext &C, ArrayRef< std::pair< unsigned, Attribute > > Attrs)
Create an AttributeList with the specified parameters in it.
AttributeSet getRetAttrs() const
The attributes for the ret value are returned.
AttributeSet getParamAttrs(unsigned ArgNo) const
The attributes for the argument or parameter at the given index are returned.
static AttributeSet get(LLVMContext &C, const AttrBuilder &B)
LLVM Basic Block Representation.
iterator_range< const_phi_iterator > phis() const
Returns a range that iterates over the phis in the basic block.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
const Function * getParent() const
Return the enclosing method, or null if none.
InstListType::iterator iterator
Instruction iterators...
const Instruction & back() const
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
void setCallingConv(CallingConv::ID CC)
std::optional< OperandBundleUse > getOperandBundle(StringRef Name) const
Return an operand bundle by name, if present.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getCalledOperand() const
void setAttributes(AttributeList A)
Set the attributes for this call.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
FunctionType * getFunctionType() const
void removeFnAttr(Attribute::AttrKind Kind)
Removes the attribute from the function.
unsigned arg_size() const
AttributeList getAttributes() const
Return the attributes for this call.
This class represents a function call, abstracting a target machine's calling convention.
void addHandler(BasicBlock *Dest)
Add an entry to the switch instruction... Note: This action invalidates handler_end().
static ConstantTokenNone * get(LLVMContext &Context)
Return the ConstantTokenNone.
This is an important base class in LLVM.
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
void recalculate(ParentType &Func)
recalculate - compute a dominator tree for the given function
Legacy analysis pass which computes a DominatorTree.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
bool dominates(const BasicBlock *BB, const Use &U) const
Return true if the (end of the) basic block BB dominates the use U.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Module * getParent()
Get the module that this global value is contained inside of...
PointerType * getType() const
Global values are always pointers.
@ ExternalLinkage
Externally visible function.
IntegerType * getIntNTy(unsigned N)
Fetch the type representing an N-bit integer.
ConstantInt * getInt32(uint32_t C)
Get a constant 32-bit value.
ConstantInt * getIntN(unsigned N, uint64_t C)
Get a constant N-bit value, zero extended or truncated from a 64-bit value.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
This is an important class for using LLVM in a threaded context.
The landingpad instruction holds all of the information necessary to generate correct exception handl...
An instruction for reading from memory.
LLVMContext & getContext() const
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
LLVMContext & getContext() const
Get the global data context.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
static PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
Helper class for SSA formation on a set of values defined in multiple blocks.
Helper class for SSA formation on a set of values defined in multiple blocks.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
iterator find(StringRef Key)
StringRef - Represent a constant reference to a string, i.e.
static StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The instances of the Type class are immutable: once they are created, they are never changed.
static IntegerType * getInt32Ty(LLVMContext &C)
static UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
LLVM Value Representation.
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< user_iterator > users()
LLVMContext & getContext() const
All values hold a context through their type.
iterator_range< use_iterator > uses()
StringRef getName() const
Return a constant reference to the value's name.
void takeName(Value *V)
Transfer the name from V to this value.
const ParentTy * getParent() const
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
A raw_ostream that writes to an std::string.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ WASM_EmscriptenInvoke
For emscripten __invoke_* functions.
@ Tail
Attemps to make calls as fast as possible while guaranteeing that tail call optimization can always b...
@ C
The default llvm calling convention, compatible with C.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
cl::opt< bool > WasmEnableSjLj
cl::opt< bool > WasmEnableEmEH
cl::opt< bool > WasmEnableEmSjLj
This is an optimization pass for GlobalISel generic memory operations.
@ Low
Lower the current thread's priority such that it does not affect foreground tasks significantly.
StringRef getEHPersonalityName(EHPersonality Pers)
BasicBlock * changeToInvokeAndSplitBasicBlock(CallInst *CI, BasicBlock *UnwindEdge, DomTreeUpdater *DTU=nullptr)
Convert the CallInst to InvokeInst with the specified unwind edge basic block.
CallInst * changeToCall(InvokeInst *II, DomTreeUpdater *DTU=nullptr)
This function converts the specified invoke into a normal call.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
OperandBundleDefT< Value * > OperandBundleDef
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
std::tuple< const DIScope *, const DIScope *, const DILocalVariable * > VarID
A unique key that represents a debug variable.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
ModulePass * createWebAssemblyLowerEmscriptenEHSjLj()
@ Or
Bitwise or logical OR of integers.
BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="", bool Before=false)
Split the specified block at the specified instruction.
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...