Skip to content

Commit d68fee8

Browse files
committed
Perform Builtin.load and Builtin.store using the type
info for the result/argument type. This makes them properly generic over an arbitrary type. Swift SVN r1563
1 parent ee905f3 commit d68fee8

File tree

2 files changed

+160
-94
lines changed

2 files changed

+160
-94
lines changed

lib/IRGen/Explosion.h

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ class Explosion {
229229
NextValue = 0;
230230
Values.clear();
231231
}
232+
233+
void reset(ExplosionKind level) {
234+
Kind = level;
235+
reset();
236+
}
232237
};
233238

234239
/// An explosion schema is essentially the type of an Explosion.

lib/IRGen/GenFunc.cpp

+155-94
Original file line numberDiff line numberDiff line change
@@ -125,36 +125,75 @@ namespace {
125125
/// result may be indirect, in which case it is returned in memory
126126
/// whose address is passed as an implicit first argument, or it may
127127
/// be direct.
128-
struct CallResult {
129-
private:
130-
ManagedValue DirectValues[ExplosionSchema::MaxScalarsForDirectResult];
131-
unsigned char NumDirectValues;
132-
public:
133-
ExplosionKind DirectExplosionLevel;
128+
class CallResult {
129+
union Value {
130+
Explosion Direct;
131+
132+
/// The address into which to emit an indirect call. If this is
133+
/// set, the call will be evaluated (as an initialization) into
134+
/// this address; otherwise, memory will be allocated on the stack.
135+
Address Indirect;
136+
137+
Value() {}
138+
~Value() {}
139+
};
134140

135-
/// The address into which to emit an indirect call. If this is
136-
/// set, the call will be evaluated (as an initialization) into
137-
/// this address; otherwise, memory will be allocated on the stack.
138-
Address IndirectAddress;
141+
enum class State {
142+
Invalid, Indirect, Direct
143+
};
139144

140-
CallResult() : NumDirectValues(0) {}
145+
Value CurValue;
146+
State CurState;
141147

142-
void addDirectUnmanagedValue(llvm::Value *value) {
143-
addDirectValue(ManagedValue(value));
148+
public:
149+
CallResult() : CurState(State::Invalid) {}
150+
~CallResult() { reset(); }
151+
152+
/// Configure this result to carry a number of direct values at
153+
/// the given explosion level.
154+
Explosion &initForDirectValues(ExplosionKind level) {
155+
assert(CurState == State::Invalid);
156+
CurState = State::Direct;
157+
Explosion *buffer = &CurValue.Direct;
158+
return *new (buffer) Explosion(level);
144159
}
145-
void addDirectValue(ManagedValue value) {
146-
assert(NumDirectValues < ExplosionSchema::MaxScalarsForDirectResult);
147-
DirectValues[NumDirectValues++] = value;
160+
161+
/// As a potential efficiency, set that this is a direct result
162+
/// with no values.
163+
void setAsEmptyDirect() {
164+
initForDirectValues(ExplosionKind::Maximal);
148165
}
149-
ManagedValue getDirectValue(unsigned i) const {
150-
assert(i < NumDirectValues);
151-
return DirectValues[i];
166+
167+
/// Set this result so that it carries a single directly-returned
168+
/// maximally-fragile value without management.
169+
void setAsSingleDirectUnmanagedFragileValue(llvm::Value *value) {
170+
initForDirectValues(ExplosionKind::Maximal).addUnmanaged(value);
152171
}
153-
void clearDirectValues() {
154-
NumDirectValues = 0;
172+
173+
void setAsIndirectAddress(Address address) {
174+
assert(CurState == State::Invalid);
175+
CurState = State::Indirect;
176+
CurValue.Indirect = address;
155177
}
156-
llvm::ArrayRef<ManagedValue> getDirectValues() const {
157-
return ArrayRef<ManagedValue>(DirectValues, DirectValues+NumDirectValues);
178+
179+
bool isInvalid() const { return CurState == State::Invalid; }
180+
bool isDirect() const { return CurState == State::Direct; }
181+
bool isIndirect() const { return CurState == State::Indirect; }
182+
183+
Explosion &getDirectValues() {
184+
assert(isDirect());
185+
return CurValue.Direct;
186+
}
187+
188+
Address getIndirectAddress() const {
189+
assert(isIndirect());
190+
return CurValue.Indirect;
191+
}
192+
193+
void reset() {
194+
if (CurState == State::Direct)
195+
CurValue.Direct.~Explosion();
196+
CurState = State::Invalid;
158197
}
159198
};
160199

@@ -475,6 +514,15 @@ namespace {
475514
? ManagedValue(nullptr) : data);
476515
}
477516

517+
void setForIndirectCallFromResult(CallResult &result) {
518+
assert(result.isDirect());
519+
Explosion &values = result.getDirectValues();
520+
assert(values.size() == 2);
521+
llvm::Value *fn = values.claimUnmanagedNext();
522+
ManagedValue data = values.claimNext();
523+
setForIndirectCall(fn, data);
524+
}
525+
478526
llvm::Value *getOpaqueFunctionPointer(IRGenFunction &IGF) const {
479527
return IGF.Builder.CreateBitCast(FnPtr, IGF.IGM.Int8PtrTy);
480528
}
@@ -550,15 +598,24 @@ namespace {
550598
};
551599
}
552600

553-
static void emitCastBuiltin(llvm::Instruction::CastOps Opcode,
601+
static void emitCastBuiltin(llvm::Instruction::CastOps opcode,
554602
IRGenFunction &IGF, ArgList &args,
555603
Type DestType, CallResult &result) {
556-
llvm::Value *Input = args.Values.claimUnmanagedNext();
557-
llvm::Type *DestTy = IGF.IGM.getFragileTypeInfo(DestType).getStorageType();
604+
llvm::Value *input = args.Values.claimUnmanagedNext();
605+
llvm::Type *destTy = IGF.IGM.getFragileTypeInfo(DestType).getStorageType();
558606
assert(args.Values.empty() && "wrong operands to cast operation");
559-
return result.addDirectUnmanagedValue(IGF.Builder.CreateCast(Opcode, Input,
560-
DestTy));
607+
llvm::Value *output = IGF.Builder.CreateCast(opcode, input, destTy);
608+
result.setAsSingleDirectUnmanagedFragileValue(output);
609+
}
561610

611+
/// Given an address representing an unsafe pointer to the given type,
612+
/// turn it into a valid Address.
613+
static Address getAddressForUnsafePointer(IRGenFunction &IGF,
614+
const TypeInfo &type,
615+
llvm::Value *addr) {
616+
llvm::Value *castAddr =
617+
IGF.Builder.CreateBitCast(addr, type.getStorageType()->getPointerTo());
618+
return Address(castAddr, type.StorageAlignment);
562619
}
563620

564621

@@ -578,50 +635,43 @@ static void emitBuiltinCall(IRGenFunction &IGF, FuncDecl *Fn,
578635
llvm::Value *lhs = args.Values.claimUnmanagedNext();
579636
llvm::Value *rhs = args.Values.claimUnmanagedNext();
580637
assert(args.Values.empty() && "wrong operands to gep operation");
581-
return result.addDirectUnmanagedValue(IGF.Builder.CreateGEP(lhs, rhs));
638+
639+
// We don't expose a non-inbounds GEP operation.
640+
llvm::Value *gep = IGF.Builder.CreateInBoundsGEP(lhs, rhs);
641+
return result.setAsSingleDirectUnmanagedFragileValue(gep);
582642
}
583643

584644
case BuiltinValueKind::Load: {
585-
llvm::Value *addr = args.Values.claimUnmanagedNext();
645+
// The type of the operation is the result type of the load function.
646+
Type valueTy = Fn->getType()->castTo<FunctionType>()->getResult();
647+
const TypeInfo &valueTI = IGF.IGM.getFragileTypeInfo(valueTy);
648+
649+
// Treat the raw pointer as a physical l-value of that type.
650+
llvm::Value *addrValue = args.Values.claimUnmanagedNext();
651+
Address addr = getAddressForUnsafePointer(IGF, valueTI, addrValue);
586652
assert(args.Values.empty() && "wrong operands to load operation");
587653

588-
Type ResultTy = Fn->getType()->castTo<FunctionType>()->getResult();
589-
590-
// Cast the raw pointer to an appropriately-typed pointer.
591-
const TypeInfo &ResultTypeInfo = IGF.IGM.getFragileTypeInfo(ResultTy);
592-
llvm::Type *ptr = ResultTypeInfo.StorageType->getPointerTo();
593-
addr = IGF.Builder.CreateBitCast(addr, ptr);
594-
595-
// Create the load.
596-
llvm::Value *load
597-
= IGF.Builder.CreateLoad(addr, ResultTypeInfo.StorageAlignment);
598-
599-
if (ResultTy->is<BuiltinObjectPointerType>())
600-
return result.addDirectValue(IGF.enterReleaseCleanup(load));
601-
602-
return result.addDirectUnmanagedValue(load);
654+
// Perform the load.
655+
Explosion &out = result.initForDirectValues(ExplosionKind::Maximal);
656+
return valueTI.load(IGF, addr, out);
603657
}
604658

605659
case BuiltinValueKind::Store: {
606-
llvm::Value *value = args.Values.forwardNext(IGF);
607-
llvm::Value *addr = args.Values.claimUnmanagedNext();
608-
assert(args.Values.empty() && "wrong operands to store operation");
609-
610-
// Extract the type of the value we're storing.
611-
Type ValueTy = Fn->getType()->castTo<FunctionType>()->getInput()
660+
// The type of the operation is the type of the first argument of
661+
// the store function.
662+
Type valueTy = Fn->getType()->castTo<FunctionType>()->getInput()
612663
->castTo<TupleType>()->getElementType(0);
613-
const TypeInfo &ValueTypeInfo = IGF.IGM.getFragileTypeInfo(ValueTy);
614-
615-
// Cast the raw pointer to an appropriately-typed pointer.
616-
addr = IGF.Builder.CreateBitCast(addr, value->getType()->getPointerTo());
617-
618-
// Create the store.
619-
if (ValueTy->is<BuiltinObjectPointerType>())
620-
IGF.emitAssignRetained(value,
621-
Address(addr, ValueTypeInfo.StorageAlignment));
622-
else
623-
IGF.Builder.CreateStore(value, addr, ValueTypeInfo.StorageAlignment);
624-
return;
664+
const TypeInfo &valueTI = IGF.IGM.getFragileTypeInfo(valueTy);
665+
666+
// Treat the raw pointer as a physical l-value of that type.
667+
llvm::Value *addrValue = args.Values.takeLast().getUnmanagedValue();
668+
Address addr = getAddressForUnsafePointer(IGF, valueTI, addrValue);
669+
670+
// Mark that we're not returning anything.
671+
result.setAsEmptyDirect();
672+
673+
// Perform the assignment operation.
674+
return valueTI.assign(IGF, args.Values, addr);
625675
}
626676

627677
/// A macro which expands to the emission of a simple binary operation
@@ -630,7 +680,8 @@ static void emitBuiltinCall(IRGenFunction &IGF, FuncDecl *Fn,
630680
llvm::Value *lhs = args.Values.claimUnmanagedNext(); \
631681
llvm::Value *rhs = args.Values.claimUnmanagedNext(); \
632682
assert(args.Values.empty() && "wrong operands to binary operation"); \
633-
return result.addDirectUnmanagedValue(IGF.Builder.Create##Op(lhs, rhs));\
683+
return result.setAsSingleDirectUnmanagedFragileValue( \
684+
IGF.Builder.Create##Op(lhs, rhs)); \
634685
}
635686

636687
/// A macro which expands to the emission of a simple binary operation
@@ -640,10 +691,10 @@ static void emitBuiltinCall(IRGenFunction &IGF, FuncDecl *Fn,
640691
llvm::Value *rhs = args.Values.claimUnmanagedNext(); \
641692
assert(args.Values.empty() && "wrong operands to binary operation"); \
642693
if (lhs->getType()->isFloatingPointTy()) { \
643-
return result.addDirectUnmanagedValue( \
694+
return result.setAsSingleDirectUnmanagedFragileValue( \
644695
IGF.Builder.Create##FPOp(lhs, rhs)); \
645696
} else { \
646-
return result.addDirectUnmanagedValue( \
697+
return result.setAsSingleDirectUnmanagedFragileValue( \
647698
IGF.Builder.Create##IntOp(lhs, rhs)); \
648699
} \
649700
}
@@ -852,6 +903,14 @@ static void extractScalarResults(IRGenFunction &IGF, llvm::Value *call,
852903
/// Emit a function call.
853904
void CallPlan::emit(IRGenFunction &IGF, CallResult &result,
854905
const TypeInfo &resultType) {
906+
// Save the final result address if one was given, then reset
907+
// to establish the invariant that the result is always invalid.
908+
Address finalAddress;
909+
if (result.isIndirect()) {
910+
finalAddress = result.getIndirectAddress();
911+
result.reset();
912+
}
913+
855914
// 1. Emit the function expression.
856915
Callee callee;
857916

@@ -869,9 +928,8 @@ void CallPlan::emit(IRGenFunction &IGF, CallResult &result,
869928
if (CallSites.size() == 1) return;
870929

871930
// Otherwise, pop that call site off and set up the callee conservatively.
872-
callee.setForIndirectCall(result.getDirectValue(0).getUnmanagedValue(),
873-
result.getDirectValue(1));
874-
result.clearDirectValues();
931+
callee.setForIndirectCallFromResult(result);
932+
result.reset();
875933

876934
// Otherwise, compute information about the function we're calling.
877935
} else {
@@ -950,16 +1008,20 @@ void CallPlan::emit(IRGenFunction &IGF, CallResult &result,
9501008
assert(CallSites.empty() && "aggregate result on non-final call?");
9511009

9521010
// Force there to be an indirect address.
953-
if (!result.IndirectAddress.isValid()) {
1011+
Address resultAddress;
1012+
if (finalAddress.isValid()) {
1013+
resultAddress = finalAddress;
1014+
} else {
9541015
indirectResultObject = indirectInit.getObjectForTemporary();
9551016
indirectInit.registerObject(IGF, indirectResultObject,
9561017
NotOnHeap, resultTI);
957-
result.IndirectAddress =
1018+
resultAddress =
9581019
indirectInit.emitLocalAllocation(IGF, indirectResultObject,
9591020
NotOnHeap, resultTI,
9601021
"call.aggresult");
9611022
}
962-
args[0] = result.IndirectAddress.getAddress();
1023+
args[0] = resultAddress.getAddress();
1024+
result.setAsIndirectAddress(resultAddress);
9631025
}
9641026

9651027
// Make the call.
@@ -975,33 +1037,29 @@ void CallPlan::emit(IRGenFunction &IGF, CallResult &result,
9751037
indirectInit.markInitialized(IGF, indirectResultObject);
9761038

9771039
setAggResultAttributes(call);
978-
assert(result.getDirectValues().empty());
979-
assert(result.IndirectAddress.isValid());
1040+
assert(result.isIndirect());
9801041
return;
9811042
}
9821043

9831044
// Extract out the scalar results.
984-
if (!call->getType()->isVoidTy()) {
985-
Explosion resultExplosion(callee.ExplosionLevel);
986-
extractScalarResults(IGF, call, resultTI, resultExplosion);
987-
for (auto value : resultExplosion.claimAll())
988-
result.addDirectValue(value);
1045+
if (call->getType()->isVoidTy()) {
1046+
result.setAsEmptyDirect();
1047+
} else {
1048+
Explosion &out = result.initForDirectValues(callee.ExplosionLevel);
1049+
extractScalarResults(IGF, call, resultTI, out);
9891050
}
9901051

9911052
// If this is the end of the call sites, we're done.
9921053
if (CallSites.empty()) {
993-
assert(!result.IndirectAddress.isValid() &&
1054+
assert(!result.isIndirect() &&
9941055
"returning direct values when indirect result was requested!");
995-
result.DirectExplosionLevel = callee.ExplosionLevel;
9961056
return;
9971057
}
9981058

9991059
// Otherwise, we must have gotten a function back. Set ourselves
10001060
// up to call it, then continue emitting calls.
1001-
assert(result.getDirectValues().size() == 2);
1002-
callee.setForIndirectCall(result.getDirectValue(0).getUnmanagedValue(),
1003-
result.getDirectValue(1));
1004-
result.clearDirectValues();
1061+
callee.setForIndirectCallFromResult(result);
1062+
result.reset();
10051063
}
10061064
}
10071065

@@ -1016,16 +1074,19 @@ void swift::irgen::emitApplyExpr(IRGenFunction &IGF, ApplyExpr *E,
10161074
plan.emit(IGF, result, resultTI);
10171075

10181076
// If this was an indirect return, explode it.
1019-
if (result.IndirectAddress.isValid()) {
1020-
return resultTI.load(IGF, result.IndirectAddress, explosion);
1077+
if (result.isIndirect()) {
1078+
return resultTI.load(IGF, result.getIndirectAddress(), explosion);
10211079
}
10221080

1023-
if (result.DirectExplosionLevel == explosion.getKind())
1024-
return explosion.add(result.getDirectValues());
1081+
Explosion &directValues = result.getDirectValues();
1082+
1083+
// If the explosion kind of the direct values matches that of the
1084+
// result, we're okay.
1085+
if (directValues.getKind() == explosion.getKind())
1086+
return explosion.add(directValues.claimAll());
10251087

1026-
Explosion resultExplosion(result.DirectExplosionLevel);
1027-
resultExplosion.add(result.getDirectValues());
1028-
resultTI.reexplode(IGF, resultExplosion, explosion);
1088+
// Otherwise we need to re-explode.
1089+
resultTI.reexplode(IGF, directValues, explosion);
10291090
}
10301091

10311092
/// See whether we can emit the result of the given call as an object
@@ -1043,8 +1104,8 @@ swift::irgen::tryEmitApplyAsAddress(IRGenFunction &IGF, ApplyExpr *E,
10431104

10441105
CallResult result;
10451106
plan.emit(IGF, result, resultTI);
1046-
assert(result.IndirectAddress.isValid());
1047-
return result.IndirectAddress;
1107+
assert(result.isIndirect());
1108+
return result.getIndirectAddress();
10481109
}
10491110

10501111
/// Emit a nullary call to the given function, using the standard

0 commit comments

Comments
 (0)