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 F->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 (F->hasFnAttribute("wasm-import-module")) {

450 B.addAttribute("wasm-import-module", "env");

451 F->addFnAttrs(B);

452 }

453 if (F->hasFnAttribute("wasm-import-name")) {

455 B.addAttribute("wasm-import-name", F->getName());

456 F->addFnAttrs(B);

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);

870 if (BB->getParent() != F)

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;

973 SS << *U;

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 (II)

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";

1397 SS << I;

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;

1424 I != IE; ++I) {

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

1435 I != IE; ++I) {

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 (F.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...