|
28 | 28 | #include "llvm/Support/MathExtras.h"
|
29 | 29 | #include "llvm/Support/circular_raw_ostream.h"
|
30 | 30 | #include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
| 31 | +#include "llvm/Transforms/Utils/PromoteMemToReg.h" |
31 | 32 |
|
32 | 33 | using namespace llvm;
|
33 | 34 |
|
@@ -1110,11 +1111,176 @@ static Instruction *lowerNonLocalAlloca(CoroAllocaAllocInst *AI,
|
1110 | 1111 | return cast<Instruction>(Alloc);
|
1111 | 1112 | }
|
1112 | 1113 |
|
| 1114 | +/// Get the current swifterror value. |
| 1115 | +static Value *emitGetSwiftErrorValue(IRBuilder<> &Builder, Type *ValueTy, |
| 1116 | + coro::Shape &Shape) { |
| 1117 | + // Make a fake function pointer as a sort of intrinsic. |
| 1118 | + auto FnTy = FunctionType::get(ValueTy, {}, false); |
| 1119 | + auto Fn = ConstantPointerNull::get(FnTy->getPointerTo()); |
| 1120 | + |
| 1121 | + auto Call = Builder.CreateCall(Fn, {}); |
| 1122 | + Shape.SwiftErrorOps.push_back(Call); |
| 1123 | + |
| 1124 | + return Call; |
| 1125 | +} |
| 1126 | + |
| 1127 | +/// Set the given value as the current swifterror value. |
| 1128 | +/// |
| 1129 | +/// Returns a slot that can be used as a swifterror slot. |
| 1130 | +static Value *emitSetSwiftErrorValue(IRBuilder<> &Builder, Value *V, |
| 1131 | + coro::Shape &Shape) { |
| 1132 | + // Make a fake function pointer as a sort of intrinsic. |
| 1133 | + auto FnTy = FunctionType::get(V->getType()->getPointerTo(), |
| 1134 | + {V->getType()}, false); |
| 1135 | + auto Fn = ConstantPointerNull::get(FnTy->getPointerTo()); |
| 1136 | + |
| 1137 | + auto Call = Builder.CreateCall(Fn, { V }); |
| 1138 | + Shape.SwiftErrorOps.push_back(Call); |
| 1139 | + |
| 1140 | + return Call; |
| 1141 | +} |
| 1142 | + |
| 1143 | +/// Set the swifterror value from the given alloca before a call, |
| 1144 | +/// then put in back in the alloca afterwards. |
| 1145 | +/// |
| 1146 | +/// Returns an address that will stand in for the swifterror slot |
| 1147 | +/// until splitting. |
| 1148 | +static Value *emitSetAndGetSwiftErrorValueAround(Instruction *Call, |
| 1149 | + AllocaInst *Alloca, |
| 1150 | + coro::Shape &Shape) { |
| 1151 | + auto ValueTy = Alloca->getAllocatedType(); |
| 1152 | + IRBuilder<> Builder(Call); |
| 1153 | + |
| 1154 | + // Load the current value from the alloca and set it as the |
| 1155 | + // swifterror value. |
| 1156 | + auto ValueBeforeCall = Builder.CreateLoad(ValueTy, Alloca); |
| 1157 | + auto Addr = emitSetSwiftErrorValue(Builder, ValueBeforeCall, Shape); |
| 1158 | + |
| 1159 | + // Move to after the call. Since swifterror only has a guaranteed |
| 1160 | + // value on normal exits, we can ignore implicit and explicit unwind |
| 1161 | + // edges. |
| 1162 | + if (isa<CallInst>(Call)) { |
| 1163 | + Builder.SetInsertPoint(Call->getNextNode()); |
| 1164 | + } else { |
| 1165 | + auto Invoke = cast<InvokeInst>(Call); |
| 1166 | + Builder.SetInsertPoint(Invoke->getNormalDest()->getFirstNonPHIOrDbg()); |
| 1167 | + } |
| 1168 | + |
| 1169 | + // Get the current swifterror value and store it to the alloca. |
| 1170 | + auto ValueAfterCall = emitGetSwiftErrorValue(Builder, ValueTy, Shape); |
| 1171 | + Builder.CreateStore(ValueAfterCall, Alloca); |
| 1172 | + |
| 1173 | + return Addr; |
| 1174 | +} |
| 1175 | + |
| 1176 | +/// Eliminate a formerly-swifterror alloca by inserting the get/set |
| 1177 | +/// intrinsics and attempting to MemToReg the alloca away. |
| 1178 | +static void eliminateSwiftErrorAlloca(Function &F, AllocaInst *Alloca, |
| 1179 | + coro::Shape &Shape) { |
| 1180 | + for (auto UI = Alloca->use_begin(), UE = Alloca->use_end(); UI != UE; ) { |
| 1181 | + // We're likely changing the use list, so use a mutation-safe |
| 1182 | + // iteration pattern. |
| 1183 | + auto &Use = *UI; |
| 1184 | + ++UI; |
| 1185 | + |
| 1186 | + // swifterror values can only be used in very specific ways. |
| 1187 | + // We take advantage of that here. |
| 1188 | + auto User = Use.getUser(); |
| 1189 | + if (isa<LoadInst>(User) || isa<StoreInst>(User)) |
| 1190 | + continue; |
| 1191 | + |
| 1192 | + assert(isa<CallInst>(User) || isa<InvokeInst>(User)); |
| 1193 | + auto Call = cast<Instruction>(User); |
| 1194 | + |
| 1195 | + auto Addr = emitSetAndGetSwiftErrorValueAround(Call, Alloca, Shape); |
| 1196 | + |
| 1197 | + // Use the returned slot address as the call argument. |
| 1198 | + Use.set(Addr); |
| 1199 | + } |
| 1200 | + |
| 1201 | + // All the uses should be loads and stores now. |
| 1202 | + assert(isAllocaPromotable(Alloca)); |
| 1203 | +} |
| 1204 | + |
| 1205 | +/// "Eliminate" a swifterror argument by reducing it to the alloca case |
| 1206 | +/// and then loading and storing in the prologue and epilog. |
| 1207 | +/// |
| 1208 | +/// The argument keeps the swifterror flag. |
| 1209 | +static void eliminateSwiftErrorArgument(Function &F, Argument &Arg, |
| 1210 | + coro::Shape &Shape, |
| 1211 | + SmallVectorImpl<AllocaInst*> &AllocasToPromote) { |
| 1212 | + IRBuilder<> Builder(F.getEntryBlock().getFirstNonPHIOrDbg()); |
| 1213 | + |
| 1214 | + auto ArgTy = cast<PointerType>(Arg.getType()); |
| 1215 | + auto ValueTy = ArgTy->getElementType(); |
| 1216 | + |
| 1217 | + // Reduce to the alloca case: |
| 1218 | + |
| 1219 | + // Create an alloca and replace all uses of the arg with it. |
| 1220 | + auto Alloca = Builder.CreateAlloca(ValueTy, ArgTy->getAddressSpace()); |
| 1221 | + Arg.replaceAllUsesWith(Alloca); |
| 1222 | + |
| 1223 | + // Set an initial value in the alloca. swifterror is always null on entry. |
| 1224 | + auto InitialValue = Constant::getNullValue(ValueTy); |
| 1225 | + Builder.CreateStore(InitialValue, Alloca); |
| 1226 | + |
| 1227 | + // Find all the suspends in the function and save and restore around them. |
| 1228 | + for (auto Suspend : Shape.CoroSuspends) { |
| 1229 | + (void) emitSetAndGetSwiftErrorValueAround(Suspend, Alloca, Shape); |
| 1230 | + } |
| 1231 | + |
| 1232 | + // Find all the coro.ends in the function and restore the error value. |
| 1233 | + for (auto End : Shape.CoroEnds) { |
| 1234 | + Builder.SetInsertPoint(End); |
| 1235 | + auto FinalValue = Builder.CreateLoad(ValueTy, Alloca); |
| 1236 | + (void) emitSetSwiftErrorValue(Builder, FinalValue, Shape); |
| 1237 | + } |
| 1238 | + |
| 1239 | + // Now we can use the alloca logic. |
| 1240 | + AllocasToPromote.push_back(Alloca); |
| 1241 | + eliminateSwiftErrorAlloca(F, Alloca, Shape); |
| 1242 | +} |
| 1243 | + |
| 1244 | +/// Eliminate all problematic uses of swifterror arguments and allocas |
| 1245 | +/// from the function. We'll fix them up later when splitting the function. |
| 1246 | +static void eliminateSwiftError(Function &F, coro::Shape &Shape) { |
| 1247 | + SmallVector<AllocaInst*, 4> AllocasToPromote; |
| 1248 | + |
| 1249 | + // Look for a swifterror argument. |
| 1250 | + for (auto &Arg : F.args()) { |
| 1251 | + if (!Arg.hasSwiftErrorAttr()) continue; |
| 1252 | + |
| 1253 | + eliminateSwiftErrorArgument(F, Arg, Shape, AllocasToPromote); |
| 1254 | + break; |
| 1255 | + } |
| 1256 | + |
| 1257 | + // Look for swifterror allocas. |
| 1258 | + for (auto &Inst : F.getEntryBlock()) { |
| 1259 | + auto Alloca = dyn_cast<AllocaInst>(&Inst); |
| 1260 | + if (!Alloca || !Alloca->isSwiftError()) continue; |
| 1261 | + |
| 1262 | + // Clear the swifterror flag. |
| 1263 | + Alloca->setSwiftError(false); |
| 1264 | + |
| 1265 | + AllocasToPromote.push_back(Alloca); |
| 1266 | + eliminateSwiftErrorAlloca(F, Alloca, Shape); |
| 1267 | + } |
| 1268 | + |
| 1269 | + // If we have any allocas to promote, compute a dominator tree and |
| 1270 | + // promote them en masse. |
| 1271 | + if (!AllocasToPromote.empty()) { |
| 1272 | + DominatorTree DT(F); |
| 1273 | + PromoteMemToReg(AllocasToPromote, DT); |
| 1274 | + } |
| 1275 | +} |
| 1276 | + |
1113 | 1277 | void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
|
1114 | 1278 | // Lower coro.dbg.declare to coro.dbg.value, since we are going to rewrite
|
1115 | 1279 | // access to local variables.
|
1116 | 1280 | LowerDbgDeclare(F);
|
1117 | 1281 |
|
| 1282 | + eliminateSwiftError(F, Shape); |
| 1283 | + |
1118 | 1284 | if (Shape.ABI == coro::ABI::Switch &&
|
1119 | 1285 | Shape.SwitchLowering.PromiseAlloca) {
|
1120 | 1286 | Shape.getSwitchCoroId()->clearPromise();
|
|
0 commit comments