Skip to content

Commit

Permalink
Use memcpy when copying structs without copy ctor (#601)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Jun 18, 2024
1 parent 42a3510 commit 8050f73
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 45 deletions.
1 change: 1 addition & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
version: 2
project_name: spice
builds:
- main: ./.github/media/dummy.go
Expand Down
2 changes: 1 addition & 1 deletion .run/spice build.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice build" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="build -O0 -d -g ../../src-bootstrap/main.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice build" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="build -O0 -d -g ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_ADDITIONAL_FLAGS" value="-lole32 -lws2_32" />
<env name="LLVM_BUILD_INCLUDE_DIR" value="$PROJECT_DIR$/../llvm-project-latest/build/include" />
Expand Down
2 changes: 1 addition & 1 deletion .run/spice run.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d -ir ../../src-bootstrap/main.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d -ir ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_ADDITIONAL_FLAGS" value="-lole32 -lws2_32" />
<env name="LLVM_BUILD_INCLUDE_DIR" value="$PROJECT_DIR$/../llvm-project-latest/build/include" />
Expand Down
2 changes: 1 addition & 1 deletion .run/spice test.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice test" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="test -O0 -d -ir ../../src-bootstrap/main.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice test" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="test -O0 -d -ir ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_ADDITIONAL_FLAGS" value="-lole32 -lws2_32" />
<env name="LLVM_BUILD_INCLUDE_DIR" value="$PROJECT_DIR$/../llvm-project-latest/build/include" />
Expand Down
10 changes: 9 additions & 1 deletion media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
f<int> main() {
int Test = 123;

type TestStruct struct {
int a
TestStruct* b
}

f<int> main() {
TestStruct testStruct = TestStruct{1, nil<TestStruct*>};
TestStruct testCopy = testStruct;
}

/*import "std/os/env";
Expand Down
2 changes: 1 addition & 1 deletion src/SourceFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ void SourceFile::visualizerOutput(std::string outputName, const std::string &out
void SourceFile::printStatusMessage(const char *stage, const CompileStageIOType &in, const CompileStageIOType &out,
uint64_t stageRuntime, unsigned short stageRuns) const {
if (cliOptions.printDebugOutput) {
const char *const compilerStageIoTypeName[] = {"Code", "Tokens", "CST", "AST", "IR", "OBJECT_FILE"};
const char *const compilerStageIoTypeName[6] = {"Code", "Tokens", "CST", "AST", "IR", "Obj"};
// Build output string
std::stringstream outputStr;
outputStr << "[" << stage << "] for " << fileName << ": ";
Expand Down
9 changes: 4 additions & 5 deletions src/irgenerator/IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,10 @@ llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbol

// Struct
if (symbolType.is(TY_STRUCT)) {
// Retrieve struct type
// Retrieve field count
Scope *structScope = symbolType.getBodyScope();
assert(structScope != nullptr);
const size_t fieldCount = structScope->getFieldCount();
auto structType = reinterpret_cast<llvm::StructType *>(symbolType.toLLVMType(sourceFile));

// Get default values for all fields of the struct
std::vector<llvm::Constant *> fieldConstants;
Expand All @@ -268,14 +267,14 @@ llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbol

fieldConstants.push_back(defaultFieldValue);
}

auto structType = reinterpret_cast<llvm::StructType *>(symbolType.toLLVMType(sourceFile));
return llvm::ConstantStruct::get(structType, fieldConstants);
}

// Interface
if (symbolType.is(TY_INTERFACE)) {
// Retrieve struct type
auto structType = reinterpret_cast<llvm::StructType *>(symbolType.toLLVMType(sourceFile));

return llvm::ConstantStruct::get(structType, llvm::Constant::getNullValue(builder.getPtrTy()));
}

Expand Down Expand Up @@ -395,7 +394,7 @@ LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, SymbolTableEnt
const QualType &rhsSType, bool isDecl) {
// Deduce some information about the assignment
const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getQualType().isRef();
const bool needsCopy = !isDecl && !isRefAssign && rhsSType.removeReferenceWrapper().is(TY_STRUCT) && !rhs.isTemporary();
const bool needsCopy = !isRefAssign && rhsSType.removeReferenceWrapper().is(TY_STRUCT) && !rhs.isTemporary();

if (isRefAssign) {
assert(lhsEntry != nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,24 @@ define dso_local i32 @main() #1 {
%2 = load i1, ptr %b_addr, align 1
%3 = zext i1 %2 to i32
%4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %1, i32 %3)
%5 = load %struct.StructToCopy, ptr %stc, align 4
store %struct.StructToCopy %5, ptr %stc2, align 4
call void @llvm.memcpy.p0.p0.i64(ptr %stc2, ptr %stc, i64 8, i1 false)
%a_addr1 = getelementptr inbounds %struct.StructToCopy, ptr %stc2, i64 0, i32 0
%6 = load i32, ptr %a_addr1, align 4
%5 = load i32, ptr %a_addr1, align 4
%b_addr2 = getelementptr inbounds %struct.StructToCopy, ptr %stc2, i64 0, i32 1
%7 = load i1, ptr %b_addr2, align 1
%8 = zext i1 %7 to i32
%9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %6, i32 %8)
%10 = load i32, ptr %result, align 4
ret i32 %10
%6 = load i1, ptr %b_addr2, align 1
%7 = zext i1 %6 to i32
%8 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %5, i32 %7)
%9 = load i32, ptr %result, align 4
ret i32 %9
}

; Function Attrs: nofree nounwind
declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #3

attributes #0 = { norecurse }
attributes #1 = { noinline nounwind optnone uwtable }
attributes #2 = { nofree nounwind }
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@ define dso_local i32 @main() #0 {
%1 = call %struct.Socket @_Z16openServerSockett(i16 8080)
store %struct.Socket %1, ptr %s, align 8
%nested_addr = getelementptr inbounds %struct.Socket, ptr %s, i64 0, i32 2
%2 = load %struct.NestedSocket, ptr %nested_addr, align 8
store %struct.NestedSocket %2, ptr %n, align 8
call void @llvm.memcpy.p0.p0.i64(ptr %n, ptr %nested_addr, i64 16, i1 false)
%testString_addr = getelementptr inbounds %struct.NestedSocket, ptr %n, i64 0, i32 0
%3 = load ptr, ptr %testString_addr, align 8
%4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, ptr %3)
%2 = load ptr, ptr %testString_addr, align 8
%3 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, ptr %2)
%sock_addr = getelementptr inbounds %struct.Socket, ptr %s, i64 0, i32 0
%5 = load i32, ptr %sock_addr, align 4
%6 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %5)
%7 = load i32, ptr %result, align 4
ret i32 %7
%4 = load i32, ptr %sock_addr, align 4
%5 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %4)
%6 = load i32, ptr %result, align 4
ret i32 %6
}

declare %struct.Socket @_Z16openServerSockett(i16)

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nofree nounwind
declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1
declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2

attributes #0 = { noinline nounwind optnone uwtable }
attributes #1 = { nofree nounwind }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nofree nounwind }
37 changes: 20 additions & 17 deletions test/test-files/irgenerator/structs/success-struct/ir-code.ll
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,31 @@ define dso_local i32 @main() #0 {
store double 4.634000e+01, ptr %2, align 8
%3 = getelementptr inbounds %struct.TestStruct, ptr %instance, i32 0, i32 2
store ptr %nestedInstance, ptr %3, align 8
%4 = load %struct.TestStruct, ptr %instance, align 8
store %struct.TestStruct %4, ptr %instance1, align 8
call void @llvm.memcpy.p0.p0.i64(ptr %instance1, ptr %instance, i64 24, i1 false)
%nested_addr = getelementptr inbounds %struct.TestStruct, ptr %instance, i64 0, i32 2
%5 = load ptr, ptr %nested_addr, align 8
%nested2_addr = getelementptr inbounds %struct.Nested, ptr %5, i64 0, i32 1
%6 = load ptr, ptr %nested2_addr, align 8
%7 = load i1, ptr %6, align 1
%8 = zext i1 %7 to i32
%4 = load ptr, ptr %nested_addr, align 8
%nested2_addr = getelementptr inbounds %struct.Nested, ptr %4, i64 0, i32 1
%5 = load ptr, ptr %nested2_addr, align 8
%6 = load i1, ptr %5, align 1
%7 = zext i1 %6 to i32
%field2_addr = getelementptr inbounds %struct.TestStruct, ptr %instance1, i64 0, i32 1
%9 = load double, ptr %field2_addr, align 8
%10 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %8, double %9)
%8 = load double, ptr %field2_addr, align 8
%9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %7, double %8)
%nested_addr1 = getelementptr inbounds %struct.TestStruct, ptr %instance1, i64 0, i32 2
%11 = load ptr, ptr %nested_addr1, align 8
%nested1_addr = getelementptr inbounds %struct.Nested, ptr %11, i64 0, i32 0
%12 = load ptr, ptr %nested1_addr, align 8
%13 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, ptr %12)
%14 = load i32, ptr %result, align 4
ret i32 %14
%10 = load ptr, ptr %nested_addr1, align 8
%nested1_addr = getelementptr inbounds %struct.Nested, ptr %10, i64 0, i32 0
%11 = load ptr, ptr %nested1_addr, align 8
%12 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, ptr %11)
%13 = load i32, ptr %result, align 4
ret i32 %13
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nofree nounwind
declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1
declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2

attributes #0 = { noinline nounwind optnone uwtable }
attributes #1 = { nofree nounwind }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nofree nounwind }

0 comments on commit 8050f73

Please sign in to comment.