Skip to content

[sil] [irgen] make object instruction valid #30736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ namespace {
StructLayout *createLayoutWithTailElems(IRGenModule &IGM,
SILType classType,
ArrayRef<SILType> tailTypes) const;

using HeapTypeInfo<ClassTypeInfo>::initialize;
void initialize(IRGenFunction &IGF, Explosion &e, Address addr,
bool isOutlined) const override;
};
} // end anonymous namespace

Expand Down Expand Up @@ -483,6 +487,29 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType,
return *Layout;
}

void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr,
bool isOutlined) const {
// If the address is a poitner to the exploded type then we can simply emit a
// store and be done.
auto *exploded = src.getAll().front();
if (exploded->getType() == addr->getType()->getPointerElementType()) {
IGF.Builder.CreateStore(exploded, addr);
(void)src.claimNext();
return;
}

// If both are the same (address) type then just emit a memcpy.
if (exploded->getType() == addr->getType()) {
IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(),
addr.getAlignment());
(void)src.claimNext();
return;
}

// Otherwise, bail to the default implementation.
HeapTypeInfo<ClassTypeInfo>::initialize(IGF, src, addr, isOutlined);
}

/// Cast the base to i8*, apply the given inbounds offset (in bytes,
/// as a size_t), and cast to a pointer to the given type.
llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base,
Expand Down
47 changes: 44 additions & 3 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,9 +877,7 @@ class IRGenSILFunction :
void visitDestroyValueInst(DestroyValueInst *i);
void visitAutoreleaseValueInst(AutoreleaseValueInst *i);
void visitSetDeallocatingInst(SetDeallocatingInst *i);
void visitObjectInst(ObjectInst *i) {
llvm_unreachable("object instruction cannot appear in a function");
}
void visitObjectInst(ObjectInst *i);
void visitStructInst(StructInst *i);
void visitTupleInst(TupleInst *i);
void visitEnumInst(EnumInst *i);
Expand Down Expand Up @@ -3450,6 +3448,49 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) {
.consume(*this, in, getDefaultAtomicity());
}

void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) {
SILType objType = i->getType().getObjectType();
// TODO: Currently generic classes aren't allowed but in the future we could
// support this.
assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() ||
!objType.getClassOrBoundGenericClass()
->getAsGenericContext()
->isGeneric() &&
"Generics are not yet supported");

const auto &typeInfo = cast<LoadableTypeInfo>(getTypeInfo(objType));
llvm::Value *metadata = emitClassHeapMetadataRef(
*this, objType.getASTType(), MetadataValueType::TypeMetadata,
MetadataState::Complete);
// TODO: this shouldn't be a stack alloc but, in order to maintain
// compatibility with alloc_ref we have to be a pointer.
Address alloca =
createAlloca(typeInfo.getStorageType()->getPointerElementType(),
typeInfo.getFixedAlignment());
auto classAddr = Builder.CreateBitCast(alloca, IGM.RefCountedPtrTy);
auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(),
"reference.new");
classVal = Builder.CreateBitCast(classVal, typeInfo.getStorageType());
// Match each property in the class decl to elements in the object
// instruction.
auto propsArr =
i->getType().getClassOrBoundGenericClass()->getStoredProperties();
SmallVector<VarDecl *, 8> props(propsArr.begin(), propsArr.end());
for (SILValue elt : i->getAllElements()) {
auto prop = props.pop_back_val();
auto elementExplosion = getLoweredExplosion(elt);
auto propType = IGM.getLoweredType(prop->getType());
const auto &propTypeInfo = cast<LoadableTypeInfo>(getTypeInfo(propType));
auto propAddr = projectPhysicalClassMemberAddress(*this, classVal, objType,
propType, prop);
propTypeInfo.initialize(*this, elementExplosion, propAddr, false);
}

Explosion e;
e.add(classVal);
setLoweredExplosion(i, e);
}

void IRGenSILFunction::visitStructInst(swift::StructInst *i) {
Explosion out;
for (SILValue elt : i->getElements())
Expand Down
8 changes: 6 additions & 2 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1809,8 +1809,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
checkGlobalAccessInst(GVI);
}

void checkObjectInst(ObjectInst *) {
require(false, "object instruction is only allowed in a static initializer");
void checkObjectInst(ObjectInst *objectInst) {
auto *classDecl = objectInst->getType().getClassOrBoundGenericClass();
require(classDecl, "Type of object instruction must be a class");
require(!classDecl->getAsGenericContext() ||
!classDecl->getAsGenericContext()->isGeneric(),
"Generics are not yet supported in object instructions");
}

void checkIntegerLiteralInst(IntegerLiteralInst *ILI) {
Expand Down
3 changes: 3 additions & 0 deletions lib/SILOptimizer/Transforms/DeadStoreElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,9 @@ void DSEContext::processLoadInst(SILInstruction *I, DSEKind Kind) {

void DSEContext::processStoreInst(SILInstruction *I, DSEKind Kind) {
auto *SI = cast<StoreInst>(I);
// TODO: for some reason these stores are removed when they shouldn't be.
if (isa<ObjectInst>(SI->getSrc()))
return;
processWrite(I, SI->getSrc(), SI->getDest(), Kind);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
case SILInstructionKind::MarkUninitializedInst:
llvm_unreachable("not valid in canonical sil");
case SILInstructionKind::ObjectInst:
llvm_unreachable("not valid in a function");
return InlineCost::Free;
}

llvm_unreachable("Unhandled ValueKind in switch.");
Expand Down
105 changes: 105 additions & 0 deletions test/IRGen/object.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// RUN: %swift -disable-legacy-type-info -module-name run %s -emit-ir -o - | %FileCheck %s

// REQUIRES: CODEGENERATOR=X86

sil_stage canonical

import Builtin
import Swift
import SwiftShims

class Foo {
@_hasStorage @_hasInitialValue var x: Int { get set }
@objc deinit
init()
}

class Bar {
@_hasStorage @_hasInitialValue var x: Int { get set }
@_hasStorage @_hasInitialValue var y: Int { get set }
@_hasStorage @_hasInitialValue var z: Int { get set }
@objc deinit
init()
}

// CHECK-LABEL: define swiftcc i64 @foouser
// CHECK: entry
// CHECK: [[ALLOC:%.*]] = alloca %T3run3FooC
// CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$s3run3FooCMa"
// CHECK: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]]
// CHECK: [[REF:%.*]] = bitcast %T3run3FooC* [[ALLOC]] to %swift.refcounted*
// CHECK: [[OBJ_R:%.*]] = call %swift.refcounted* @swift_initStackObject({{.*}}[[META_E]], {{.*}}[[REF]])
// CHECK: [[OBJ:%.*]] = bitcast %swift.refcounted* [[OBJ_R]] to %T3run3FooC*

// CHECK: [[X_PTR:%.*]] = getelementptr inbounds %T3run3FooC, %T3run3FooC* [[OBJ]], i32 0, i32 1
// CHECK: [[X:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR]], i32 0, i32 0
// CHECK: store i64 0, i64* [[X]]

// CHECK: [[X_PTR1:%.*]] = getelementptr inbounds %T3run3FooC, %T3run3FooC* [[OBJ]], i32 0, i32 1
// CHECK: [[X1:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0
// CHECK: [[X_VAL:%.*]] = load i64, i64* [[X1]]
// CHECK: [[ADDED:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[X_VAL]], i64 1)
// CHECK: [[SUM:%.*]] = extractvalue { i64, i1 } [[ADDED]], 0

// CHECK: [[X2:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0
// CHECK: store i64 [[SUM]], i64* [[X2]]
// CHECK: [[X3:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0
// CHECK: [[OUT:%.*]] = load i64, i64* [[X3]]
// CHECK: ret i64 [[OUT]]
sil @foouser : $@convention(thin) () -> Int {
bb0:
%1 = integer_literal $Builtin.Int64, 0 // user: %2
%2 = struct $Int (%1 : $Builtin.Int64) // user: %3
%3 = object $Foo (%2 : $Int) // user: %4
%4 = ref_element_addr %3 : $Foo, #Foo.x // users: %17, %5
%5 = begin_access [modify] [dynamic] [no_nested_conflict] %4 : $*Int // users: %7, %15, %16
%6 = integer_literal $Builtin.Int64, 1 // user: %10
%7 = struct_element_addr %5 : $*Int, #Int._value // user: %8
%8 = load %7 : $*Builtin.Int64 // user: %10
%9 = integer_literal $Builtin.Int1, -1 // user: %10
%10 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %6 : $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %12, %11
%11 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 0 // user: %14
%12 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 1 // user: %13
cond_fail %12 : $Builtin.Int1, "arithmetic overflow" // id: %13
%14 = struct $Int (%11 : $Builtin.Int64) // user: %15
store %14 to %5 : $*Int // id: %15
end_access %5 : $*Int // id: %16
%17 = begin_access [read] [static] [no_nested_conflict] %4 : $*Int // users: %19, %18
%18 = load %17 : $*Int // user: %20
end_access %17 : $*Int // id: %19
return %18 : $Int // id: %20
} // end sil function 'foouser'

sil_vtable Foo {

}

// CHECK-LABEL: define swiftcc i64 @baruser
// CHECK: [[ALLOC:%.*]] = alloca %T3run3BarC
// CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$s3run3BarCMa"
// CHECK: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]]
// CHECK: [[REF:%.*]] = bitcast %T3run3BarC* [[ALLOC]] to %swift.refcounted*
// CHECK: [[OBJ_R:%.*]] = call %swift.refcounted* @swift_initStackObject({{.*}}[[META_E]], {{.*}}[[REF]])
// CHECK: [[OBJ:%.*]] = bitcast %swift.refcounted* [[OBJ_R]] to %T3run3BarC*
// CHECK: [[X_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 3
// CHECK: [[X:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR]], i32 0, i32 0
// CHECK: store i64 0, i64* [[X]]
// CHECK: [[Y_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 2
// CHECK: [[Y:%.*]] = getelementptr inbounds %TSi, %TSi* [[Y_PTR]], i32 0, i32 0
// CHECK: store i64 0, i64* [[Y]]
// CHECK: [[Z_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 1
// CHECK: [[Z:%.*]] = getelementptr inbounds %TSi, %TSi* [[Z_PTR]], i32 0, i32 0
// CHECK: store i64 0, i64* [[Z]]
sil @baruser : $@convention(thin) () -> Int {
bb0:
%1 = integer_literal $Builtin.Int64, 0
%2 = struct $Int (%1 : $Builtin.Int64)
%3 = object $Bar (%2 : $Int, %2 : $Int, %2 : $Int)

return %2 : $Int
}


sil_vtable Bar {

}