|
24 | 24 | #include "GenCall.h"
|
25 | 25 | #include "GenDecl.h"
|
26 | 26 | #include "GenMeta.h"
|
| 27 | +#include "GenOpaque.h" |
27 | 28 | #include "GenProto.h"
|
28 | 29 | #include "GenType.h"
|
29 | 30 | #include "IRGenDebugInfo.h"
|
@@ -66,8 +67,7 @@ llvm::Value *irgen::emitDistributedActorInitializeRemote(
|
66 | 67 | namespace {
|
67 | 68 |
|
68 | 69 | struct AllocationInfo {
|
69 |
| - SILType Type; |
70 |
| - const TypeInfo &TI; |
| 70 | + unsigned ArgumentIdx; |
71 | 71 | StackAddress Addr;
|
72 | 72 | };
|
73 | 73 |
|
@@ -97,6 +97,30 @@ class DistributedAccessor {
|
97 | 97 | llvm::Value *argumentTypes,
|
98 | 98 | Explosion &arguments);
|
99 | 99 |
|
| 100 | + /// Load an argument value from the given buffer \c argumentSlot |
| 101 | + /// to the given explosion \c arguments. Type of the argument is |
| 102 | + /// the same as parameter type. |
| 103 | + /// |
| 104 | + /// Returns a pair of aligned offset and value size. |
| 105 | + std::pair<llvm::Value *, llvm::Value *> |
| 106 | + loadArgument(unsigned argumentIdx, |
| 107 | + llvm::Value *argumentSlot, |
| 108 | + SILType paramType, |
| 109 | + ParameterConvention convention, |
| 110 | + Explosion &arguments); |
| 111 | + |
| 112 | + /// Load an argument value from the given buffer \c argumentSlot |
| 113 | + /// to the given explosion \c arguments. Information describing |
| 114 | + /// the type of argument comes from runtime metadata. |
| 115 | + /// |
| 116 | + /// Returns a pair of aligned offset and value size. |
| 117 | + std::pair<llvm::Value *, llvm::Value *> |
| 118 | + loadArgument(unsigned argumentIdx, |
| 119 | + llvm::Value *argumentSlot, |
| 120 | + llvm::Value *argumentType, |
| 121 | + ParameterConvention convention, |
| 122 | + Explosion &arguments); |
| 123 | + |
100 | 124 | FunctionPointer getPointerToTarget() const;
|
101 | 125 |
|
102 | 126 | Callee getCalleeForDistributedTarget(llvm::Value *self) const;
|
@@ -204,88 +228,191 @@ void DistributedAccessor::computeArguments(llvm::Value *argumentBuffer,
|
204 | 228 | // Initialize "offset" with the address of the base of the argument buffer.
|
205 | 229 | IGF.Builder.CreateStore(argumentBuffer, offset);
|
206 | 230 |
|
207 |
| - for (const auto ¶m : parameters) { |
| 231 | + // Cast type buffer to `swift.type**` |
| 232 | + argumentTypes = |
| 233 | + IGF.Builder.CreateBitCast(argumentTypes, IGM.TypeMetadataPtrPtrTy); |
| 234 | + |
| 235 | + for (unsigned i = 0, n = parameters.size(); i != n; ++i) { |
| 236 | + const auto ¶m = parameters[i]; |
208 | 237 | auto paramTy = param.getSILStorageInterfaceType();
|
209 |
| - const TypeInfo &typeInfo = IGF.getTypeInfo(paramTy); |
210 | 238 |
|
211 |
| - // 1. Load current offset. |
212 |
| - llvm::Value *currentOffset = IGF.Builder.CreateLoad(offset, "elt_offset"); |
| 239 | + // The size of the loaded argument value |
| 240 | + llvm::Value *valueSize; |
213 | 241 |
|
214 |
| - // 2. Cast the pointer to the type of the element. |
215 |
| - Address eltPtr = IGF.Builder.CreateBitCast( |
216 |
| - Address(currentOffset, IGM.getPointerAlignment()), |
217 |
| - IGM.getStoragePointerType(paramTy)); |
| 242 | + // Load current offset |
| 243 | + llvm::Value *currentOffset = IGF.Builder.CreateLoad(offset, "elt_offset"); |
218 | 244 |
|
219 |
| - // 3. Adjust typed pointer to the alignment of the type. |
220 |
| - auto alignedOffset = typeInfo.roundUpToTypeAlignment(IGF, eltPtr, paramTy); |
| 245 | + // Load argument value into the provided explosion |
| 246 | + if (paramTy.hasTypeParameter()) { |
| 247 | + auto offset = |
| 248 | + Size(i * IGM.DataLayout.getTypeAllocSize(IGM.TypeMetadataPtrTy)); |
| 249 | + auto alignment = |
| 250 | + IGM.DataLayout.getABITypeAlignment(IGM.TypeMetadataPtrTy); |
| 251 | + |
| 252 | + // Load metadata describing argument value from argument types buffer. |
| 253 | + auto typeLoc = IGF.emitAddressAtOffset( |
| 254 | + argumentTypes, Offset(offset), IGM.TypeMetadataPtrTy, |
| 255 | + Alignment(alignment), "arg_type_loc"); |
| 256 | + |
| 257 | + auto *argumentTy = IGF.Builder.CreateLoad(typeLoc, "arg_type"); |
| 258 | + |
| 259 | + // Load argument value based using loaded type metadata. |
| 260 | + std::tie(currentOffset, valueSize) = loadArgument( |
| 261 | + i, currentOffset, argumentTy, param.getConvention(), arguments); |
| 262 | + } else { |
| 263 | + std::tie(currentOffset, valueSize) = loadArgument( |
| 264 | + i, currentOffset, paramTy, param.getConvention(), arguments); |
| 265 | + } |
221 | 266 |
|
222 |
| - if (paramTy.isObject()) { |
223 |
| - auto &nativeSchema = typeInfo.nativeParameterValueSchema(IGM); |
224 |
| - // If schema is empty, skip to the next argument. |
225 |
| - if (nativeSchema.empty()) |
226 |
| - continue; |
| 267 | + // Move the offset to the beginning of the next element, unless |
| 268 | + // this is the last element |
| 269 | + if (param != parameters.back()) { |
| 270 | + llvm::Value *addr = IGF.Builder.CreatePtrToInt(currentOffset, IGM.IntPtrTy); |
| 271 | + llvm::Value *nextOffset = IGF.Builder.CreateIntToPtr( |
| 272 | + IGF.Builder.CreateAdd(addr, valueSize), IGM.Int8PtrTy); |
| 273 | + IGF.Builder.CreateStore(nextOffset, offset); |
227 | 274 | }
|
| 275 | + } |
228 | 276 |
|
229 |
| - // 4. Create an exploded version of the type to pass as an |
230 |
| - // argument to distributed method. |
| 277 | + IGF.Builder.CreateLifetimeEnd(offset, IGM.getPointerSize()); |
| 278 | +} |
231 | 279 |
|
232 |
| - switch (param.getConvention()) { |
233 |
| - case ParameterConvention::Indirect_In: |
234 |
| - case ParameterConvention::Indirect_In_Constant: { |
235 |
| - // The +1 argument is passed indirectly, so we need to copy it into |
236 |
| - // a temporary. |
| 280 | +std::pair<llvm::Value *, llvm::Value *> |
| 281 | +DistributedAccessor::loadArgument(unsigned argumentIdx, |
| 282 | + llvm::Value *argumentSlot, |
| 283 | + llvm::Value *argumentType, |
| 284 | + ParameterConvention convention, |
| 285 | + Explosion &arguments) { |
| 286 | + // TODO: `emitLoad*` would actually load value witness table every |
| 287 | + // time it's called, which is sub-optimal but all of the APIs that |
| 288 | + // deal with value witness tables are currently hidden in GenOpaque.cpp |
| 289 | + llvm::Value *valueSize = emitLoadOfSize(IGF, argumentType); |
| 290 | + |
| 291 | + llvm::Value *isInline, *flags; |
| 292 | + std::tie(isInline, flags) = emitLoadOfIsInline(IGF, argumentType); |
| 293 | + |
| 294 | + llvm::Value *alignmentMask = emitAlignMaskFromFlags(IGF, flags); |
| 295 | + |
| 296 | + // Align argument value offset as required by its type metadata. |
| 297 | + llvm::Value *alignedSlot = |
| 298 | + IGF.Builder.CreatePtrToInt(argumentSlot, IGM.IntPtrTy); |
| 299 | + { |
| 300 | + alignedSlot = IGF.Builder.CreateNUWAdd(alignedSlot, alignmentMask); |
237 | 301 |
|
238 |
| - auto stackAddr = typeInfo.allocateStack(IGF, paramTy, "arg.temp"); |
239 |
| - auto argPtr = stackAddr.getAddress().getAddress(); |
| 302 | + llvm::Value *invertedMask = IGF.Builder.CreateNot(alignmentMask); |
| 303 | + alignedSlot = IGF.Builder.CreateAnd(alignedSlot, invertedMask); |
| 304 | + alignedSlot = |
| 305 | + IGF.Builder.CreateIntToPtr(alignedSlot, IGM.OpaquePtrTy); |
| 306 | + } |
240 | 307 |
|
241 |
| - typeInfo.initializeWithCopy(IGF, stackAddr.getAddress(), alignedOffset, |
242 |
| - paramTy, /*isOutlined=*/false); |
243 |
| - arguments.add(argPtr); |
| 308 | + switch (convention) { |
| 309 | + case ParameterConvention::Indirect_In: |
| 310 | + case ParameterConvention::Indirect_In_Constant: { |
| 311 | + // The +1 argument is passed indirectly, so we need to copy it into |
| 312 | + // a temporary. |
244 | 313 |
|
245 |
| - // Remember to deallocate later. |
246 |
| - AllocatedArguments.push_back({paramTy, typeInfo, stackAddr}); |
247 |
| - break; |
248 |
| - } |
| 314 | + auto stackAddr = |
| 315 | + IGF.emitDynamicAlloca(IGM.Int8Ty, valueSize, Alignment(16)); |
| 316 | + auto argPtr = stackAddr.getAddress().getAddress(); |
249 | 317 |
|
250 |
| - case ParameterConvention::Indirect_In_Guaranteed: { |
251 |
| - // The argument is +0, so we can use the address of the param in |
252 |
| - // the context directly. |
253 |
| - arguments.add(alignedOffset.getAddress()); |
254 |
| - break; |
255 |
| - } |
| 318 | + emitInitializeWithCopyCall(IGF, argumentType, stackAddr.getAddress(), |
| 319 | + Address(alignedSlot, IGM.getPointerAlignment())); |
| 320 | + arguments.add(argPtr); |
256 | 321 |
|
257 |
| - case ParameterConvention::Indirect_Inout: |
258 |
| - case ParameterConvention::Indirect_InoutAliasable: |
259 |
| - llvm_unreachable("indirect parameters are not supported"); |
| 322 | + // Remember to deallocate later. |
| 323 | + AllocatedArguments.push_back({argumentIdx, stackAddr}); |
| 324 | + break; |
| 325 | + } |
260 | 326 |
|
261 |
| - case ParameterConvention::Direct_Guaranteed: |
262 |
| - case ParameterConvention::Direct_Unowned: { |
263 |
| - cast<LoadableTypeInfo>(typeInfo).loadAsTake(IGF, alignedOffset, |
264 |
| - arguments); |
265 |
| - break; |
266 |
| - } |
| 327 | + case ParameterConvention::Indirect_In_Guaranteed: { |
| 328 | + // The argument is +0, so we can use the address of the param in |
| 329 | + // the context directly. |
| 330 | + arguments.add(alignedSlot); |
| 331 | + break; |
| 332 | + } |
267 | 333 |
|
268 |
| - case ParameterConvention::Direct_Owned: |
269 |
| - // Copy the value out at +1. |
270 |
| - cast<LoadableTypeInfo>(typeInfo).loadAsCopy(IGF, alignedOffset, |
271 |
| - arguments); |
272 |
| - } |
| 334 | + case ParameterConvention::Indirect_Inout: |
| 335 | + case ParameterConvention::Indirect_InoutAliasable: |
| 336 | + llvm_unreachable("indirect 'inout' parameters are not supported"); |
273 | 337 |
|
274 |
| - // 6. Move the offset to the beginning of the next element, unless |
275 |
| - // this is the last element. |
276 |
| - if (param != parameters.back()) { |
277 |
| - llvm::Value *typeSize = typeInfo.getSize(IGF, paramTy); |
| 338 | + case ParameterConvention::Direct_Guaranteed: |
| 339 | + case ParameterConvention::Direct_Unowned: |
| 340 | + case ParameterConvention::Direct_Owned: |
| 341 | + llvm_unreachable("cannot pass opaque type directly"); |
| 342 | + } |
278 | 343 |
|
279 |
| - llvm::Value *addr = alignedOffset.getAddress(); |
280 |
| - addr = IGF.Builder.CreatePtrToInt(addr, IGM.IntPtrTy); |
281 |
| - llvm::Value *nextOffset = IGF.Builder.CreateIntToPtr( |
282 |
| - IGF.Builder.CreateAdd(addr, typeSize), IGM.Int8PtrTy); |
| 344 | + return {alignedSlot, valueSize}; |
| 345 | +} |
283 | 346 |
|
284 |
| - IGF.Builder.CreateStore(nextOffset, offset); |
285 |
| - } |
| 347 | +std::pair<llvm::Value *, llvm::Value *> |
| 348 | +DistributedAccessor::loadArgument(unsigned argumentIdx, |
| 349 | + llvm::Value *argumentSlot, |
| 350 | + SILType paramTy, |
| 351 | + ParameterConvention convention, |
| 352 | + Explosion &arguments) { |
| 353 | + const TypeInfo &typeInfo = IGF.getTypeInfo(paramTy); |
| 354 | + |
| 355 | + // 1. Check whether the native representation is empty e.g. |
| 356 | + // this happens for empty enums. |
| 357 | + if (paramTy.isObject()) { |
| 358 | + auto &nativeSchema = typeInfo.nativeParameterValueSchema(IGM); |
| 359 | + // If schema is empty, skip to the next argument. |
| 360 | + if (nativeSchema.empty()) |
| 361 | + return {argumentSlot, typeInfo.getSize(IGF, paramTy)}; |
286 | 362 | }
|
287 | 363 |
|
288 |
| - IGF.Builder.CreateLifetimeEnd(offset, IGM.getPointerSize()); |
| 364 | + // 2. Cast the pointer to the type of the element. |
| 365 | + Address eltPtr = IGF.Builder.CreateBitCast( |
| 366 | + Address(argumentSlot, IGM.getPointerAlignment()), |
| 367 | + IGM.getStoragePointerType(paramTy)); |
| 368 | + |
| 369 | + // 3. Adjust typed pointer to the alignment of the type. |
| 370 | + auto alignedOffset = typeInfo.roundUpToTypeAlignment(IGF, eltPtr, paramTy); |
| 371 | + |
| 372 | + // 4. Create an exploded version of the type to pass as an |
| 373 | + // argument to distributed method. |
| 374 | + |
| 375 | + switch (convention) { |
| 376 | + case ParameterConvention::Indirect_In: |
| 377 | + case ParameterConvention::Indirect_In_Constant: { |
| 378 | + // The +1 argument is passed indirectly, so we need to copy it into |
| 379 | + // a temporary. |
| 380 | + |
| 381 | + auto stackAddr = typeInfo.allocateStack(IGF, paramTy, "arg.temp"); |
| 382 | + auto argPtr = stackAddr.getAddress().getAddress(); |
| 383 | + |
| 384 | + typeInfo.initializeWithCopy(IGF, stackAddr.getAddress(), alignedOffset, |
| 385 | + paramTy, /*isOutlined=*/false); |
| 386 | + arguments.add(argPtr); |
| 387 | + |
| 388 | + // Remember to deallocate later. |
| 389 | + AllocatedArguments.push_back({argumentIdx, stackAddr}); |
| 390 | + break; |
| 391 | + } |
| 392 | + |
| 393 | + case ParameterConvention::Indirect_In_Guaranteed: { |
| 394 | + // The argument is +0, so we can use the address of the param in |
| 395 | + // the context directly. |
| 396 | + arguments.add(alignedOffset.getAddress()); |
| 397 | + break; |
| 398 | + } |
| 399 | + |
| 400 | + case ParameterConvention::Indirect_Inout: |
| 401 | + case ParameterConvention::Indirect_InoutAliasable: |
| 402 | + llvm_unreachable("indirect 'inout' parameters are not supported"); |
| 403 | + |
| 404 | + case ParameterConvention::Direct_Guaranteed: |
| 405 | + case ParameterConvention::Direct_Unowned: { |
| 406 | + cast<LoadableTypeInfo>(typeInfo).loadAsTake(IGF, alignedOffset, arguments); |
| 407 | + break; |
| 408 | + } |
| 409 | + |
| 410 | + case ParameterConvention::Direct_Owned: |
| 411 | + // Copy the value out at +1. |
| 412 | + cast<LoadableTypeInfo>(typeInfo).loadAsCopy(IGF, alignedOffset, arguments); |
| 413 | + } |
| 414 | + |
| 415 | + return {alignedOffset.getAddress(), typeInfo.getSize(IGF, paramTy)}; |
289 | 416 | }
|
290 | 417 |
|
291 | 418 | void DistributedAccessor::emit() {
|
@@ -385,8 +512,18 @@ void DistributedAccessor::emit() {
|
385 | 512 |
|
386 | 513 | // Deallocate all of the copied arguments.
|
387 | 514 | {
|
388 |
| - for (auto &entry : AllocatedArguments) |
389 |
| - entry.TI.deallocateStack(IGF, entry.Addr, entry.Type); |
| 515 | + auto targetType = Target->getLoweredFunctionType(); |
| 516 | + for (auto &alloca : AllocatedArguments) { |
| 517 | + const auto ¶m = targetType->getParameters()[alloca.ArgumentIdx]; |
| 518 | + auto paramTy = param.getSILStorageInterfaceType(); |
| 519 | + |
| 520 | + if (paramTy.hasTypeParameter()) { |
| 521 | + IGF.emitDeallocateDynamicAlloca(alloca.Addr); |
| 522 | + } else { |
| 523 | + auto &typeInfo = IGF.getTypeInfo(paramTy); |
| 524 | + typeInfo.deallocateStack(IGF, alloca.Addr, paramTy); |
| 525 | + } |
| 526 | + } |
390 | 527 | }
|
391 | 528 |
|
392 | 529 | Explosion voidResult;
|
|
0 commit comments