diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index 4fe5b4f95..8c78c64b3 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -23,9 +23,9 @@ ABIArgInfo *abi_arg_new_direct_coerce(AbiType *target_type); ABIArgInfo *abi_arg_new_expand_coerce(AbiType *target_type, unsigned offset); ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType *first_element, unsigned initial_offset, AbiType *second_element, unsigned padding, bool is_packed); ABIArgInfo *abi_arg_new_expand_padded(Type *padding); -ABIArgInfo *abi_arg_new_indirect_realigned(unsigned alignment, Type *by_val_type); +ABIArgInfo *abi_arg_new_indirect_realigned(AlignSize alignment, Type *by_val_type); ABIArgInfo *abi_arg_new_indirect_by_val(Type *by_val_type); -ABIArgInfo *abi_arg_new_indirect_not_by_val(void); +ABIArgInfo *abi_arg_new_indirect_not_by_val(Type *type); ByteSize abi_type_abi_alignment(AbiType *type); bool abi_type_is_integer(AbiType *type); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index a9a8b0e8c..4a8bb78e8 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1416,6 +1416,7 @@ typedef struct ABIArgInfo_ bool by_reg : 1; bool zeroext : 1; bool signext : 1; + bool realign : 1; } attributes; union { @@ -1453,7 +1454,7 @@ typedef struct ABIArgInfo_ struct { // We may request a certain alignment of the parameters. - AlignSize realignment; + AlignSize alignment; Type *by_val_type; } indirect; }; diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 0255adbd1..b3f918766 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -84,11 +84,13 @@ bool abi_arg_is_indirect(ABIArgInfo *info) UNREACHABLE } -ABIArgInfo *abi_arg_new_indirect_realigned(unsigned alignment, Type *by_val_type) +ABIArgInfo *abi_arg_new_indirect_realigned(AlignSize alignment, Type *by_val_type) { assert(alignment > 0); ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT); - info->indirect.realignment = alignment; + info->indirect.alignment = alignment; + assert(info->indirect.alignment); + info->attributes.realign = true; info->indirect.by_val_type = by_val_type; return info; } @@ -96,13 +98,17 @@ ABIArgInfo *abi_arg_new_indirect_realigned(unsigned alignment, Type *by_val_type ABIArgInfo *abi_arg_new_indirect_by_val(Type *by_val_type) { ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT); + info->indirect.alignment = type_abi_alignment(by_val_type); info->indirect.by_val_type = by_val_type; + assert(info->indirect.alignment); return info; } -ABIArgInfo *abi_arg_new_indirect_not_by_val(void) +ABIArgInfo *abi_arg_new_indirect_not_by_val(Type *type) { ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT); + info->indirect.alignment = type_abi_alignment(type); + assert(info->indirect.alignment); info->indirect.by_val_type = NULL; return info; } diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index 1d83a7d70..6867d7fbc 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -76,7 +76,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) return info; } - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index 1a0564c36..00aeab0a6 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -215,7 +215,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig } if (size > 16 || (size > 8 && !platform_target.int128)) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } return abi_arg_new_direct(); } @@ -238,7 +238,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig info->direct_coerce.elements = 2; return info; } - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } static ABIArgInfo *riscv_classify_return(Type *return_type) diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index d01e6b382..44c874141 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -14,7 +14,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto // Variable array has to be passed indirectly. if (type_is_structlike(type) && type->decl->has_variable_array) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } Type *base = NULL; @@ -35,7 +35,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto return abi_arg_new_expand(); } // Otherwise use indirect - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } if (is_vector) { @@ -49,7 +49,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto // HVAs are handled later. if (is_return || (!type_is_builtin(type->type_kind) && type->type_kind != TYPE_VECTOR)) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } // => to main handling. } @@ -60,7 +60,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto // Not 1, 2, 4, 8? Pass indirect. if (size > 8 || !is_power_of_two(size)) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } // Coerce to integer. return abi_arg_new_direct_coerce(abi_type_new_int_bits(size * 8)); @@ -74,7 +74,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto case TYPE_U128: case TYPE_I128: // Pass by val since greater than 8 bytes. - if (!is_return) return abi_arg_new_indirect_not_by_val(); + if (!is_return) return abi_arg_new_indirect_not_by_val(type); // Make i128 return in XMM0 return abi_arg_new_direct_coerce(abi_type_new_plain(type_get_vector(type_long, 2))); default: @@ -83,7 +83,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto } if (size > 8) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } return abi_arg_new_direct(); } diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 398dd39a5..63f76f1e2 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -42,7 +42,7 @@ ABIArgInfo *x64_indirect_return_result(Type *type) { if (type_is_abi_aggregate(type)) { - return abi_arg_new_indirect_not_by_val(); + return abi_arg_new_indirect_not_by_val(type); } type = type_lowering(type); if (type_is_promotable_integer(type)) diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 3aeed0d34..caa457ae3 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -50,7 +50,7 @@ static ABIArgInfo *x86_create_indirect_result(Regs *regs, Type *type, ByVal by_v { if (by_val != BY_VAL) { - ABIArgInfo *info = abi_arg_new_indirect_not_by_val(); + ABIArgInfo *info = abi_arg_new_indirect_not_by_val(type); if (regs->int_regs) { @@ -79,9 +79,9 @@ static ABIArgInfo *x86_create_indirect_result(Regs *regs, Type *type, ByVal by_v } -ABIArgInfo *create_indirect_return_x86(Regs *regs) +static ABIArgInfo *create_indirect_return_x86(Type *type, Regs *regs) { - ABIArgInfo *info = abi_arg_new_indirect_not_by_val(); + ABIArgInfo *info = abi_arg_new_indirect_not_by_val(type); if (!regs->int_regs) return info; // Consume a register for the return. regs->int_regs--; @@ -191,7 +191,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) { return abi_arg_new_direct_coerce(abi_type_new_int_bits(size * 8)); } - return create_indirect_return_x86(regs); + return create_indirect_return_x86(type, regs); } return abi_arg_new_direct(); } @@ -201,7 +201,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) // Structs with variable arrays are always indirect. if (type_is_structlike(type) && type->decl->has_variable_array) { - return create_indirect_return_x86(regs); + return create_indirect_return_x86(type, regs); } // Ignore empty struct/unions if (type_is_empty_record(type, true)) @@ -229,7 +229,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) // This is not a single field struct, so we wrap it in an int. return abi_arg_new_direct_coerce(abi_type_new_int_bits(size * 8)); } - return create_indirect_return_x86(regs); + return create_indirect_return_x86(type, regs); } // Is this small enough to need to be extended? @@ -239,7 +239,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) } // If we support something like int128, then this is an indirect return. - if (type_is_integer(type) && type_size(type) > 8) return create_indirect_return_x86(regs); + if (type_is_integer(type) && type_size(type) > 8) return create_indirect_return_x86(type, regs); // Otherwise we expect to just pass this nicely in the return. return abi_arg_new_direct(); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 39748db8c..752871aa2 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2399,12 +2399,12 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B case ABI_ARG_INDIRECT: { // If we want we could optimize for structs by doing it by reference here. - unsigned alignment = info->indirect.realignment ?: type_abi_alignment(type); + assert(info->indirect.alignment == type_abi_alignment(type) || info->attributes.realign); LLVMValueRef indirect = llvm_emit_alloca(c, llvm_get_type(c, type), - alignment, + info->indirect.alignment, "indirectarg"); - llvm_store_bevalue_aligned(c, indirect, be_value, alignment); + llvm_store_bevalue_aligned(c, indirect, be_value, info->indirect.alignment); vec_add(*args, indirect); return; } @@ -2610,14 +2610,15 @@ void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) { case ABI_ARG_INDIRECT: // 6a. We can use the stored error var if there is no redirect. - if (signature->failable && c->error_var && type_abi_alignment(return_type) < ret_info->indirect.realignment) + if (signature->failable && c->error_var && ret_info->attributes.realign) { error_var = c->error_var; vec_add(values, error_var); break; } // 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side. - AlignSize alignment = ret_info->indirect.realignment ? ret_info->indirect.realignment : type_abi_alignment(return_type); + assert(ret_info->attributes.realign || ret_info->indirect.alignment == type_abi_alignment(return_type)); + AlignSize alignment = ret_info->indirect.alignment; llvm_value_set_address_align(be_value, llvm_emit_alloca(c, llvm_get_type(c, return_type), alignment, "sretparam"), return_type, alignment); // 6c. Add the pointer to the list of arguments. diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index fb05022d2..d9fa3ac63 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -131,7 +131,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig { // A simple memcopy, with alignment respected. LLVMValueRef pointer = llvm_get_next_param(c, index); - llvm_emit_memcpy_to_decl(c, decl, pointer, info->indirect.realignment); + llvm_emit_memcpy_to_decl(c, decl, pointer, info->indirect.alignment); return; } case ABI_ARG_EXPAND_COERCE: @@ -274,7 +274,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl switch (info->kind) { case ABI_ARG_INDIRECT: - llvm_store_bevalue_aligned(c, return_out, return_value, info->indirect.realignment); + llvm_store_bevalue_aligned(c, return_out, return_value, info->indirect.alignment); llvm_emit_return_value(c, NULL); return; case ABI_ARG_IGNORE: @@ -496,24 +496,17 @@ static void llvm_emit_param_attributes(GenContext *context, LLVMValueRef functio case ABI_ARG_EXPAND_COERCE: break; case ABI_ARG_INDIRECT: - if (info->indirect.realignment) - { - llvm_attribute_add_int(context, function, attribute_align, info->indirect.realignment, index); - } if (is_return) { - llvm_attribute_add_int(context, function, attribute_align, type_abi_alignment(type_voidptr), 1); // TODO then type attributes are added to LLVM-C, use that for sret. llvm_attribute_add(context, function, attribute_sret, 1); + llvm_attribute_add_int(context, function, attribute_align, info->indirect.alignment, 1); } else { // TODO then type attributes are added to LLVM-C, use that for byval. if (info->indirect.by_val_type) llvm_attribute_add(context, function, attribute_byval, index); - if (!info->indirect.realignment) - { - llvm_attribute_add_int(context, function, attribute_align, type_abi_alignment(info->indirect.by_val_type), index); - } + llvm_attribute_add_int(context, function, attribute_align, info->indirect.alignment, index); } break; diff --git a/src/compiler/types.c b/src/compiler/types.c index 3f39216eb..1ec8a5558 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -984,7 +984,7 @@ static void type_create(const char *name, Type *location, TypeKind kind, unsigne .builtin.bytesize = (bitsize + 7) / 8, .builtin.bitsize = bitsize, .builtin.abi_alignment = align, - .builtin.pref_alignment = pref_align, + .builtin.pref_alignment = pref_align ?: align, .name = name, .canonical = location, }; @@ -1138,13 +1138,13 @@ type_create(#name_, &(shortname_), type_, bits_, target->align_ ## aligned_, tar #undef DEF_TYPE type_create("typeinfo", &t.typeinfo, TYPE_TYPEINFO, 0, 0, 0); - type_create("typeid", &t.typeid, TYPE_TYPEID, target->width_pointer, target->align_pref_pointer, target->align_pointer); - type_create("void*", &t.voidstar, TYPE_POINTER, target->width_pointer, target->align_pref_pointer, target->align_pointer); + type_create("typeid", &t.typeid, TYPE_TYPEID, target->width_pointer, target->align_pointer, target->align_pref_pointer); + type_create("void*", &t.voidstar, TYPE_POINTER, target->width_pointer, target->align_pointer, target->align_pref_pointer); create_type_cache(type_void); type_void->type_cache[0] = &t.voidstar; t.voidstar.pointer = type_void; - type_create("virtual*", &t.virtual, TYPE_VIRTUAL_ANY, target->width_pointer * 2, target->align_pref_pointer, target->align_pointer); - type_create("virtual_generic", &t.virtual_generic, TYPE_VIRTUAL, target->width_pointer * 2, target->align_pref_pointer, target->align_pointer); + type_create("virtual*", &t.virtual, TYPE_VIRTUAL_ANY, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); + type_create("virtual_generic", &t.virtual_generic, TYPE_VIRTUAL, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); type_create("compint", &t.ixx, TYPE_IXX, 0, 0, 0); type_create("compfloat", &t.fxx, TYPE_FXX, 0, 0, 0); diff --git a/test/test_suite/assert/assert_variants.c3t b/test/test_suite/assert/assert_variants.c3t index e70c71c64..9ec560afe 100644 --- a/test/test_suite/assert/assert_variants.c3t +++ b/test/test_suite/assert/assert_variants.c3t @@ -1,3 +1,4 @@ +// #target: x64_windows func int foo() { diff --git a/test/test_suite/errors/rethrow.c3t b/test/test_suite/errors/rethrow.c3t index d29d1bf2e..68a3ccc90 100644 --- a/test/test_suite/errors/rethrow.c3t +++ b/test/test_suite/errors/rethrow.c3t @@ -1,3 +1,4 @@ +// #target: x64_darwin func void! test() { diff --git a/test/test_suite/errors/rethrow_mingw.c3t b/test/test_suite/errors/rethrow_mingw.c3t new file mode 100644 index 000000000..98be0f1ba --- /dev/null +++ b/test/test_suite/errors/rethrow_mingw.c3t @@ -0,0 +1,44 @@ +// #target: x64_mingw + +module rethrow; + +func void! test() +{ + int! i; + i!!; +} + +// #expect: rethrow.ll + +define void @rethrow.test(%error_union* sret align 8 %0) +entry: + %i = alloca i32, align 4 + %i1 = alloca %error_union, align 8 + %error_var = alloca %error_union, align 8 + store %error_union zeroinitializer, %error_union* %i1, align 8 + store i32 0, i32* %i, align 4 + %err_domain = getelementptr inbounds %error_union, %error_union* %i1, i32 0, i32 0 + %1 = load i64, i64* %err_domain, align 8 + %not_err = icmp eq i64 %1, 0 + br i1 %not_err, label %after_check, label %error + +error: + %2 = bitcast %error_union* %error_var to i8* + %3 = bitcast %error_union* %i1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %2, i8* align 8 %3, i32 16, i1 false) + br label %guard_block + +after_check: + %4 = load i32, i32* %i, align 4 + br label %noerr_block + +guard_block: + %5 = bitcast %error_union* %0 to i8* + %6 = bitcast %error_union* %error_var to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %5, i8* align 8 %6, i32 16, i1 false) + ret void + +noerr_block: + store %error_union zeroinitializer, %error_union* %0, align 8 + ret void + diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 8c95fe48c..0092f8dfc 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -1,3 +1,5 @@ +// #target: x64_darwin + module test; import test2; import std::array::list; diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t new file mode 100644 index 000000000..6f7ad174a --- /dev/null +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -0,0 +1,721 @@ +// #target: x64_mingw + +module test; +import test2; +import std::array::list; +import std::array::linkedlist; +import hello_world; + + +extern func int printf(char *, ...); + +func void helloWorld() +{ + printf("helloWorld!\n"); +} +func int test_static() +{ + static int x = 1; + x++; + printf("Test static %d\n", x); + return x; +} + +struct Bobo { short b; float c; short d; short e; float f; short g; } + + +struct Blob { int z; int f; } + +union Foor +{ + long a; + char[12] b; +} + + +func int helo(double d, Bobo b) +{ + int[3] de = { 1, 2, 3 }; + Bobo c = b; + helo(1.0, c); + return 1; +} + + +func int test1(int a, int b) +{ + a = a >> b; + if (b > 128) return -1; + return a; +} + +struct Foo2 +{ + int x; +} + +func void Foo2.printme(Foo2 *foo) +{ + printf("Foo is: %d\n", foo.x); +} + +func int Foo2.mutate(Foo2 *foo) +{ + printf("Mutating"); + return ++foo.x; +} + + +extern func void test_virtual3(virtual* y); + +interface Baz +{ + func void open(); + func int close(); +} + +extern func void test_virtual2(virtual Baz* z); + +define oopsInt = test2::argh; +define oopsDouble = test2::argh; +define Argh = func int(double, Bobo); +define Argh2 = func int(double, Bobo); + + + +func int sum_us(int... x) +{ + int sum = 0; + if (x.len() == 0) return 0; + sum += x[0] + sum_us(...x[1..^1]); + return sum; +} + +define Frob = long; + +func int sumd(int[] x) +{ + int sum = 0; + for (int i = 0; i < x.len(); i++) sum += x[i]; + return sum; +} + +struct Foo +{ + int a; + int b; +} + +define getValueInt = test2::getValue; +define getValueDouble = test2::getValue; +define IntBlob = test2::Blob; +define DoubleBlob = Blob; +define getMultInt = getMult; +define getMultDouble = getMult; + +define IntArray = List; +define IntList = LinkedList; + +enum MyEnum : int +{ + HELO = 12, + WORLD = 14, + BYE = -5 +} + + +func void main() +{ + test_static(); + test_static(); + test_static(); + hello_world::hello(); + IntList list; + list.push(10); + list.push(15); + list.push(30); + for (int i = 0; i < (int)(list.len()); i++) + { + printf("Element[%d]: %d\n", i, list.get(i)); + } + list.free(); + + printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, MyEnum.elements); + + int max = MyEnum.max; + int min = MyEnum.min; + int elements = MyEnum.elements; + printf("Hello\n"); + IntArray array; + array.append(100); + array.append(200); + array.append(400); + array.push(600); + array.insertAt(2, 300); + for (int i = 0; i < (int)(array.len()); i++) + { + printf("Element[%d]: %d\n", i, array.get(i)); + } + array.free(); + IntBlob a = { 42 }; + DoubleBlob b = { 33.3 }; + printf("a was %d\n", getValueInt(a)); + printf("b was %f\n", getValueDouble(b)); + printf("Mult int was %d\n", getMultInt(25)); + printf("Mult double was %f\n", getMultDouble(3.3)); + + + helloWorld(); + Foo ddx; + int fro = 3; + int[4] x = { 1, 2, 3, 3 }; + fro += printf("1Vararg4unsplatA: %d\n", sum_us(...x)); + printf("%d\n", fro); + int[] z = &x; + int[3] de = { 1, 2, 3 }; + printf("Vararg4unsplatB: %d\n", sum_us(...&x)); + printf("Vararg4unsplatC: %d\n", sum_us(...z)); + printf("Vararg4: %d\n", sum_us(1, 2, 4, 5)); + printf("Vararg1: %d\n", sum_us(1)); + printf("Vararg0: %d\n", sum_us()); + Argh a1; + Argh2 b2; +} + +module hello_world; +import foo; + +extern func int printf(char *, ...); +define doubleMult = check; + +func void hello() +{ + printf("Hello baby\n"); + printf("Mult %f\n", doubleMult(11.1)); +} + +module foo ; + +func Type check(Type i) +{ + return i * i; +} + +module test2 ; + +struct Blob +{ + Type a; +} + +func Type getMult(Type a) +{ + return a * a; +} +Type argh = 234; + +error MyErr +{ + Type x; +} + +enum Hello : int +{ + FOO = 3, + BAR = 4, +} + +macro Hello wut() +{ + return Hello.FOO; +} + +define Bye = Hello; +define wat = wut; +define byebye = hello; + +func int hello() +{ + return 1; +} + +func Type getValue(Blob blob) +{ + return blob.a; +} + +// #expect: test.ll + +%Blob = type { i32 } +%Blob.0 = type { double } +%List = type { i64, i64, i32* } +%LinkedList = type { i64, %Node*, %Node* } +%Node = type { %Node*, %Node*, i32 } +%Foo2 = type { i32 } +%Bobo = type { i16, float, i16, i16, float, i16 } +%"virtual*" = type { i8*, i64 } +%"virtual$" = type { i8*, i64 } +%"int[]" = type { i32*, i64 } +%Foo = type { i32, i32 } + +define void @test.Foo2__printme(%Foo2* %0) +entry: + %foo = alloca %Foo2*, align 8 + store %Foo2* %0, %Foo2** %foo, align 8 + %1 = load %Foo2*, %Foo2** %foo, align 8 + %2 = getelementptr inbounds %Foo2, %Foo2* %1, i32 0, i32 0 + %3 = load i32, i32* %2, align 8 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.21, i32 0, i32 0), i32 %3) + ret void +} + +define i32 @test.Foo2__mutate(%Foo2* %0) +entry: + %foo = alloca %Foo2*, align 8 + store %Foo2* %0, %Foo2** %foo, align 8 + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.22, i32 0, i32 0)) + %2 = load %Foo2*, %Foo2** %foo, align 8 + %3 = getelementptr inbounds %Foo2, %Foo2* %2, i32 0, i32 0 + %4 = load i32, i32* %3, align 8 + %add = add i32 %4, 1 + store i32 %add, i32* %3, align 8 + ret i32 %add +} + +define void @test.helloWorld() +entry: + %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0)) + ret void +} + +define i32 @test.test_static() +entry: + %0 = load i32, i32* @test_static.x, align 4 + %add = add i32 %0, 1 + store i32 %add, i32* @test_static.x, align 4 + %1 = load i32, i32* @test_static.x, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.1, i32 0, i32 0), i32 %1) + %3 = load i32, i32* @test_static.x, align 4 + ret i32 %3 +} + +define i32 @test.helo(double %0, %Bobo* align 4 %1) + %d = alloca double, align 8 + %b = alloca %Bobo, align 4 + %de = alloca [3 x i32], align 4 + %c = alloca %Bobo, align 4 + %indirectarg = alloca %Bobo, align 4 + store double %0, double* %d, align 8 + %2 = bitcast %Bobo* %b to i8* + %3 = bitcast %Bobo* %1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %2, i8* align 4 %3, i32 20, i1 false) + %4 = bitcast [3 x i32]* %de to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %4, i8* align 4 bitcast ([3 x i32]* @.__const to i8*), i32 12, i1 false) + %5 = bitcast %Bobo* %c to i8* + %6 = bitcast %Bobo* %b to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %5, i8* align 4 %6, i32 20, i1 false) + %7 = bitcast %Bobo* %indirectarg to i8* + %8 = bitcast %Bobo* %c to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %7, i8* align 4 %8, i32 20, i1 false) + %9 = call i32 @test.helo(double 1.000000e+00, %Bobo* %indirectarg) + ret i32 1 +} + +define i32 @test.test1(i32 %0, i32 %1) + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 %0, i32* %a, align 4 + store i32 %1, i32* %b, align 4 + %2 = load i32, i32* %a, align 4 + %3 = load i32, i32* %b, align 4 + %ashr = ashr i32 %2, %3 + %4 = freeze i32 %ashr + store i32 %4, i32* %a, align 4 + %5 = load i32, i32* %b, align 4 + %gt = icmp sgt i32 %5, 128 + br i1 %gt, label %if.then, label %if.exit + + ret i32 -1 + + %6 = load i32, i32* %a, align 4 + ret i32 %6 +} + +declare void @test_virtual3(%"virtual*"* align 8) + +declare void @test_virtual2(%"virtual$"* align 8) + +define i32 @test.sum_us(%"int[]"* align 8 %0) +entry: + %x = alloca %"int[]", align 8 + %sum = alloca i32, align 4 + %vararg = alloca %"int[]", align 8 + %indirectarg = alloca %"int[]", align 8 + %1 = bitcast %"int[]"* %x to i8* + %2 = bitcast %"int[]"* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 16, i1 false) + store i32 0, i32* %sum, align 4 + %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 + %3 = load i64, i64* %len, align 8 + %eq = icmp eq i64 %3, 0 + br i1 %eq, label %if.then, label %if.exit + +if.then: + ret i32 0 + +if.exit: + %4 = load i32, i32* %sum, align 4 + %subarrayptr = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0 + %saptr = load i32*, i32** %subarrayptr, align 8 + %sarridx = getelementptr inbounds i32, i32* %saptr, i64 0 + %5 = load i32, i32* %sarridx, align 4 + %6 = load %"int[]", %"int[]"* %x, align 8 + %7 = extractvalue %"int[]" %6, 0 + %8 = extractvalue %"int[]" %6, 1 + %sub = sub i64 %8, 1 + %9 = add i64 %sub, 1 + %size = sub i64 %9, 1 + %offsetsub = getelementptr inbounds i32, i32* %7, i64 1 + %10 = insertvalue %"int[]" undef, i32* %offsetsub, 0 + %11 = insertvalue %"int[]" %10, i64 %size, 1 + %12 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 1 + %13 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 + store %"int[]" %11, %"int[]"* %indirectarg, align 8 + %14 = call i32 @test.sum_us(%"int[]"* %indirectarg) + %add = add i32 %5, %14 + %add1 = add i32 %4, %add + store i32 %add1, i32* %sum, align 4 + %15 = load i32, i32* %sum, align 4 + ret i32 %15 +} + +define i32 @test.sumd(%"int[]"* align 8 %0) +entry: + %x = alloca %"int[]", align 8 + %sum = alloca i32, align 4 + %i = alloca i32, align 4 + %1 = bitcast %"int[]"* %x to i8* + %2 = bitcast %"int[]"* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 16, i1 false) + store i32 0, i32* %sum, align 4 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: + %3 = load i32, i32* %i, align 4 + %sisiext = sext i32 %3 to i64 + %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 + %4 = load i64, i64* %len, align 8 + %lt = icmp slt i64 %sisiext, %4 + %check = icmp slt i64 %4, 0 + %siui-lt = or i1 %check, %lt + br i1 %siui-lt, label %for.body, label %for.exit + +for.body: + %5 = load i32, i32* %sum, align 4 + %subarrayptr = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0 + %saptr = load i32*, i32** %subarrayptr, align 8 + %6 = load i32, i32* %i, align 4 + %sisiext1 = sext i32 %6 to i64 + %sarridx = getelementptr inbounds i32, i32* %saptr, i64 %sisiext1 + %7 = load i32, i32* %sarridx, align 4 + %add = add i32 %5, %7 + store i32 %add, i32* %sum, align 4 + br label %for.inc + +for.inc: + %8 = load i32, i32* %i, align 4 + %add2 = add i32 %8, 1 + store i32 %add2, i32* %i, align 4 + br label %for.cond + +for.exit: + %9 = load i32, i32* %sum, align 4 + ret i32 %9 +} + +define void @main() +entry: + %list = alloca %LinkedList, align 8 + %i = alloca i32, align 4 + %max = alloca i32, align 4 + %min = alloca i32, align 4 + %elements = alloca i32, align 4 + %array = alloca %List, align 8 + %i1 = alloca i32, align 4 + %a = alloca %Blob, align 4 + %b = alloca %Blob.0, align 8 + %ddx = alloca %Foo, align 4 + %fro = alloca i32, align 4 + %x = alloca [4 x i32], align 4 + %vararg = alloca %"int[]", align 8 + %indirectarg = alloca %"int[]", align 8 + %z = alloca %"int[]", align 8 + %de = alloca [3 x i32], align 4 + %vararg12 = alloca %"int[]", align 8 + %indirectarg13 = alloca %"int[]", align 8 + %vararg14 = alloca %"int[]", align 8 + %indirectarg15 = alloca %"int[]", align 8 + %vararg16 = alloca %"int[]", align 8 + %varargslots = alloca [4 x i32], align 4 + %indirectarg17 = alloca %"int[]", align 8 + %vararg18 = alloca %"int[]", align 8 + %varargslots19 = alloca [1 x i32], align 4 + %indirectarg20 = alloca %"int[]", align 8 + %vararg21 = alloca %"int[]", align 8 + %indirectarg22 = alloca %"int[]", align 8 + %a1 = alloca i32 (double, %Bobo*)*, align 8 + %b2 = alloca i32 (double, %Bobo*)*, align 8 + %0 = call i32 @test.test_static() + %1 = call i32 @test.test_static() + %2 = call i32 @test.test_static() + call void @hello_world.hello() + %3 = bitcast %LinkedList* %list to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 24, i1 false) + call void @"std::array::linkedlist.int.LinkedList__push"(%LinkedList* %list, i32 10) + call void @"std::array::linkedlist.int.LinkedList__push"(%LinkedList* %list, i32 15) + call void @"std::array::linkedlist.int.LinkedList__push"(%LinkedList* %list, i32 30) + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: + %4 = load i32, i32* %i, align 4 + %5 = call i64 @"std::array::linkedlist.int.LinkedList__len"(%LinkedList* %list) + %uisitrunc = trunc i64 %5 to i32 + %lt = icmp slt i32 %4, %uisitrunc + br i1 %lt, label %for.body, label %for.exit + +for.body: + %6 = load i32, i32* %i, align 4 + %7 = load i32, i32* %i, align 4 + %siuiext = zext i32 %7 to i64 + %8 = call i32 @"std::array::linkedlist.int.LinkedList__get"(%LinkedList* %list, i64 %siuiext) + %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.2, i32 0, i32 0), i32 %6, i32 %8) + br label %for.inc + +for.inc: + %10 = load i32, i32* %i, align 4 + %add = add i32 %10, 1 + store i32 %add, i32* %i, align 4 + br label %for.cond + +for.exit: + call void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList* %list) + %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.str.3, i32 0, i32 0), i32 -5, i32 14, i32 3) + store i32 14, i32* %max, align 4 + store i32 -5, i32* %min, align 4 + store i32 3, i32* %elements, align 4 + %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.4, i32 0, i32 0)) + %13 = bitcast %List* %array to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %13, i8 0, i64 24, i1 false) + call void @"std::array::list.int.List__append"(%List* %array, i32 100) + call void @"std::array::list.int.List__append"(%List* %array, i32 200) + call void @"std::array::list.int.List__append"(%List* %array, i32 400) + call void @"std::array::list.int.List__push"(%List* %array, i32 600) + call void @"std::array::list.int.List__insertAt"(%List* %array, i64 2, i32 300) + store i32 0, i32* %i1, align 4 + br label %for.cond2 + +for.cond2: + %14 = load i32, i32* %i1, align 4 + %15 = call i64 @"std::array::list.int.List__len"(%List* %array) + %uisitrunc3 = trunc i64 %15 to i32 + %lt4 = icmp slt i32 %14, %uisitrunc3 + br i1 %lt4, label %for.body5, label %for.exit9 + +for.body5: + %16 = load i32, i32* %i1, align 4 + %17 = load i32, i32* %i1, align 4 + %siuiext6 = zext i32 %17 to i64 + %18 = call i32 @"std::array::list.int.List__get"(%List* %array, i64 %siuiext6) + %19 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.5, i32 0, i32 0), i32 %16, i32 %18) + br label %for.inc7 + +for.inc7: + %20 = load i32, i32* %i1, align 4 + %add8 = add i32 %20, 1 + store i32 %add8, i32* %i1, align 4 + br label %for.cond2 + +for.exit9: + call void @"std::array::list.int.List__free"(%List* %array) + %21 = bitcast %Blob* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob* @.__const.6 to i8*), i32 4, i1 false) + %22 = bitcast %Blob.0* %b to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 bitcast (%Blob.0* @.__const.7 to i8*), i32 8, i1 false) + %23 = bitcast %Blob* %a to i32* + %coerced = load i32, i32* %23, align 4 + %24 = call i32 @test2.int.getValue(i32 %coerced) + %25 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.8, i32 0, i32 0), i32 %24) + %26 = bitcast %Blob.0* %b to i64* + %coerced10 = load i64, i64* %26, align 8 + %27 = call double @test2.double.getValue(i64 %coerced10) + %28 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.9, i32 0, i32 0), double %27) + %29 = call i32 @test2.int.getMult(i32 25) + %30 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.10, i32 0, i32 0), i32 %29) + %31 = call double @test2.double.getMult(double 3.300000e+00) + %32 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.11, i32 0, i32 0), double %31) + call void @test.helloWorld() + %33 = bitcast %Foo* %ddx to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %33, i8 0, i64 8, i1 false) + store i32 3, i32* %fro, align 4 + %34 = bitcast [4 x i32]* %x to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %34, i8* align 4 bitcast ([4 x i32]* @.__const.12 to i8*), i32 16, i1 false) + %35 = load i32, i32* %fro, align 4 + %36 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 1 + %37 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 + store i64 4, i64* %36, align 8 + %38 = bitcast [4 x i32]* %x to i32* + store i32* %38, i32** %37, align 8 + %39 = bitcast %"int[]"* %indirectarg to i8* + %40 = bitcast %"int[]"* %vararg to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %39, i8* align 8 %40, i32 16, i1 false) + %41 = call i32 @test.sum_us(%"int[]"* %indirectarg) + %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.13, i32 0, i32 0), i32 %41) + %add11 = add i32 %35, %42 + store i32 %add11, i32* %fro, align 4 + %43 = load i32, i32* %fro, align 4 + %44 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.14, i32 0, i32 0), i32 %43) + %45 = bitcast [4 x i32]* %x to i32* + %46 = insertvalue %"int[]" undef, i32* %45, 0 + %47 = insertvalue %"int[]" %46, i64 4, 1 + store %"int[]" %47, %"int[]"* %z, align 8 + %48 = bitcast [3 x i32]* %de to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %48, i8* align 4 bitcast ([3 x i32]* @.__const.15 to i8*), i32 12, i1 false) + %49 = getelementptr inbounds %"int[]", %"int[]"* %vararg12, i32 0, i32 1 + %50 = getelementptr inbounds %"int[]", %"int[]"* %vararg12, i32 0, i32 0 + store i64 4, i64* %49, align 8 + %51 = bitcast [4 x i32]* %x to i32* + store i32* %51, i32** %50, align 8 + %52 = bitcast %"int[]"* %indirectarg13 to i8* + %53 = bitcast %"int[]"* %vararg12 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %52, i8* align 8 %53, i32 16, i1 false) + %54 = call i32 @test.sum_us(%"int[]"* %indirectarg13) + %55 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.16, i32 0, i32 0), i32 %54) + %56 = getelementptr inbounds %"int[]", %"int[]"* %vararg14, i32 0, i32 1 + %57 = getelementptr inbounds %"int[]", %"int[]"* %vararg14, i32 0, i32 0 + %58 = bitcast %"int[]"* %indirectarg15 to i8* + %59 = bitcast %"int[]"* %z to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %58, i8* align 8 %59, i32 16, i1 false) + %60 = call i32 @test.sum_us(%"int[]"* %indirectarg15) + %61 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.17, i32 0, i32 0), i32 %60) + %62 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 0 + store i32 1, i32* %62, align 4 + %63 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 1 + store i32 2, i32* %63, align 4 + %64 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 2 + store i32 4, i32* %64, align 4 + %65 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 3 + store i32 5, i32* %65, align 4 + %66 = getelementptr inbounds %"int[]", %"int[]"* %vararg16, i32 0, i32 1 + store i64 4, i64* %66, align 8 + %67 = getelementptr inbounds %"int[]", %"int[]"* %vararg16, i32 0, i32 0 + %68 = bitcast [4 x i32]* %varargslots to i32* + store i32* %68, i32** %67, align 8 + %69 = bitcast %"int[]"* %indirectarg17 to i8* + %70 = bitcast %"int[]"* %vararg16 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %69, i8* align 8 %70, i32 16, i1 false) + %71 = call i32 @test.sum_us(%"int[]"* %indirectarg17) + %72 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.18, i32 0, i32 0), i32 %71) + %73 = getelementptr inbounds [1 x i32], [1 x i32]* %varargslots19, i64 0, i64 0 + store i32 1, i32* %73, align 4 + %74 = getelementptr inbounds %"int[]", %"int[]"* %vararg18, i32 0, i32 1 + store i64 1, i64* %74, align 8 + %75 = getelementptr inbounds %"int[]", %"int[]"* %vararg18, i32 0, i32 0 + %76 = bitcast [1 x i32]* %varargslots19 to i32* + store i32* %76, i32** %75, align 8 + %77 = bitcast %"int[]"* %indirectarg20 to i8* + %78 = bitcast %"int[]"* %vararg18 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %77, i8* align 8 %78, i32 16, i1 false) + %79 = call i32 @test.sum_us(%"int[]"* %indirectarg20) + %80 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.19, i32 0, i32 0), i32 %79) + %81 = getelementptr inbounds %"int[]", %"int[]"* %vararg21, i32 0, i32 1 + store i64 0, i64* %81, align 8 + %82 = bitcast %"int[]"* %indirectarg22 to i8* + %83 = bitcast %"int[]"* %vararg21 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %82, i8* align 8 %83, i32 16, i1 false) + %84 = call i32 @test.sum_us(%"int[]"* %indirectarg22) + %85 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.20, i32 0, i32 0), i32 %84) + store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %a1, align 8 + store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %b2, align 8 + ret void +} + +// #expect: hello_world.ll + +define void @hello_world.hello() +entry: + %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i32 0, i32 0)) + %1 = call double @foo.double.check(double 1.110000e+01) + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0), double %1) + ret void + +// #expect: foo.double.ll + +define double @foo.double.check(double %0) +entry: + %i = alloca double, align 8 + store double %0, double* %i, align 8 + %1 = load double, double* %i, align 8 + %2 = load double, double* %i, align 8 + %fmul = fmul double %1, %2 + ret double %fmul + + +// #expect: test2.int.ll + +%Blob = type { i32 } +@test2.int.argh = global i32 234, align 4 + +define i32 @test2.int.getMult(i32 %0) +entry: + %a = alloca i32, align 4 + store i32 %0, i32* %a, align 4 + %1 = load i32, i32* %a, align 4 + %2 = load i32, i32* %a, align 4 + %mul = mul i32 %1, %2 + ret i32 %mul + +define i32 @test2.int.hello() +entry: + ret i32 1 +} + +define i32 @test2.int.getValue(i32 %0) +entry: + %blob = alloca %Blob, align 4 + %coerce = bitcast %Blob* %blob to i32* + store i32 %0, i32* %coerce, align 4 + %1 = getelementptr inbounds %Blob, %Blob* %blob, i32 0, i32 0 + %2 = load i32, i32* %1, align 4 + ret i32 %2 + + +// #expect: test2.double.ll + +%Blob = type { double } +@test2.double.argh = global double 2.340000e+02, align 8 + +define double @test2.double.getMult(double %0) +entry: + %a = alloca double, align 8 + store double %0, double* %a, align 8 + %1 = load double, double* %a, align 8 + %2 = load double, double* %a, align 8 + %fmul = fmul double %1, %2 + ret double %fmul + + +define i32 @test2.double.hello() +entry: + ret i32 1 + +define double @test2.double.getValue(i64 %0) +entry: + %blob = alloca %Blob, align 8 + %coerce = bitcast %Blob* %blob to i64* + store i64 %0, i64* %coerce, align 8 + %1 = getelementptr inbounds %Blob, %Blob* %blob, i32 0, i32 0 + %2 = load double, double* %1, align 8 + ret double %2 +