// Tests minimal use of temporaries in non-optimized builds for struct and array assignment.
// Tests `.init` assignment in particular.

// RUN: %ldc -output-ll %s -of=%t.ll && FileCheck %s < %t.ll

void opaque();
FloatStruct opaque(FloatStruct);
FloatStruct opaqueret();

struct FloatStruct {
    float[10] data;
}

FloatStruct globalStruct;

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack3fooFZv
void foo() {
    globalStruct = FloatStruct.init;
    // There should be only one memcpy.
    // CHECK: call void @llvm.memcpy
    // CHECK-NEXT: ret void
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack3hhhFZv
void hhh() {
    globalStruct = FloatStruct([1, 0, 0, 0, 0, 0, 0, 0, 0, 42]);
    // Future work: test optimized codegen (at -O0)
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack3gggFZv
void ggg() {
    globalStruct = opaqueret();
    // opaque() could be accessing globalStruct, in-place assignment not possible.
    // CHECK: sret_tmp = alloca %assign_struct_init_without_stack.FloatStruct
    // CHECK: call {{.*}}opaqueret
    // CHECK: call void @llvm.memcpy
    // CHECK-NEXT: ret void
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack5localFZv
void local() {
    FloatStruct local;
    local = opaque(local);
    // Not possible to do in-place assignment, so temporary must be created.
    // CHECK: local = alloca %assign_struct_init_without_stack.FloatStruct
    // CHECK: sret_tmp = alloca %assign_struct_init_without_stack.FloatStruct
    // CHECK: call {{.*}}opaque
    // CHECK: call void @llvm.memcpy
    // CHECK-NEXT: ret void
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack5arrayFZv
void array() {
    int[5] arr = [0,1,2,3,4];
    // Future work: test optimized codegen (at -O0)
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack6array2FKG3iZv
void array2(ref int[3] a) {
    a = [4, a[0], 6];
    // There should be a temporary!
    // CHECK: alloca [3 x i32]
    // CHECK: call void @llvm.memcpy
    // CHECK-NEXT: ret void
}

struct OpAssignStruct {
    float[10] data;
    ubyte a;

    ref OpAssignStruct opAssign(R)(auto ref R rhs) return {
        return this;
    }
}
OpAssignStruct globalOpAssignStruct;
OpAssignStruct globalOpAssignStruct2;

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack16tupleassignByValFZv
void tupleassignByVal()
{
    globalOpAssignStruct = OpAssignStruct.init;
    // There should be one memcpy to a temporary.
    // CHECK: alloca %assign_struct_init_without_stack.OpAssignStruct
    // CHECK: call void @llvm.memcpy
    // CHECK-NOT: memcpy
    // CHECK: call{{.*}} %assign_struct_init_without_stack.OpAssignStruct* @{{.*}}_D32assign_struct_init_without_stack14OpAssignStruct__T8opAssignTSQCmQBhZQsMFNaNbNcNiNjNfQyZQBb
    // CHECK-NEXT: ret void
}

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack16tupleassignByRefFZv
void tupleassignByRef()
{
    globalOpAssignStruct = globalOpAssignStruct2;
    // There should not be a memcpy.
    // CHECK-NOT: memcpy
    // CHECK: call{{.*}} %assign_struct_init_without_stack.OpAssignStruct* @{{.*}}_D32assign_struct_init_without_stack14OpAssignStruct__T8opAssignTSQCmQBhZQsMFNaNbNcNiNjNfKQzZQBc
    // CHECK-NEXT: ret void
}

struct DtorStruct {
    float[10] data;
    ~this() { opaque(); }
}
struct CtorStruct {
    float[10] data;
    this(int i) { opaque(); }
}
DtorStruct dtorStruct;
CtorStruct ctorStruct;

// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack4ctorFZv
void ctor() {
    ctorStruct = ctorStruct.init;
    // There is no dtor, so can be optimized to only a memcpy.
    // CHECK-NEXT: call void @llvm.memcpy
    // CHECK-NEXT: ret void
}
// CHECK-LABEL: define{{.*}} @{{.*}}_D32assign_struct_init_without_stack4dtorFZv
void dtor() {
    dtorStruct = dtorStruct.init;
    // There should be a temporary and a call to opAssign
    // CHECK: alloca %assign_struct_init_without_stack.DtorStruct
    // CHECK: call void @llvm.memcpy{{.*}}_D32assign_struct_init_without_stack10DtorStruct6__initZ
    // CHECK-NEXT: call {{.*}}_D32assign_struct_init_without_stack10DtorStruct8opAssignMFNcNjSQCkQBfZQi
    // CHECK-NEXT: ret void
}
