diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 83af69fd9ce8..e170d7b5f095 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -318,6 +318,51 @@ auto FunctionContext::GetReturnTypeInfo(TypeInFile type) return result; } +// Given a type used for an LLVM value, return the type that we use to store +// that value in memory. This is the same type unless the type is a +// non-multiple-of-8 integer type, which we explicitly widen to a multiple of 8 +// for Clang compatibility and to make our generated IR easier for LLVM to +// handle. +static auto GetWidenedMemoryType(llvm::Type* type) -> llvm::Type* { + if (auto* int_type = dyn_cast(type)) { + auto width = llvm::alignToPowerOf2(int_type->getBitWidth(), 8); + if (width != int_type->getBitWidth()) { + return llvm::IntegerType::get(type->getContext(), width); + } + } + return type; +} + +auto FunctionContext::LoadObject(TypeInFile type, llvm::Value* addr, + llvm::Twine name) -> llvm::Value* { + auto* llvm_type = GetType(type); + auto* load_type = GetWidenedMemoryType(llvm_type); + + // TODO: Include alias and alignment information. + llvm::Value* value = builder().CreateLoad(load_type, addr, name); + + if (load_type != llvm_type) { + value = builder().CreateTrunc(value, llvm_type); + } + return value; +} + +auto FunctionContext::StoreObject(TypeInFile type, llvm::Value* value, + llvm::Value* addr) -> void { + // TODO: Include alias and alignment information. + auto* llvm_type = GetType(type); + CARBON_CHECK(value->getType() == llvm_type); + + // Don't emit a store of `iN` if N is not a multiple of 8. See `LoadObject`. + auto* store_type = GetWidenedMemoryType(llvm_type); + if (store_type != llvm_type) { + // TODO: Should we consider creating a sext if the value is signed? + value = builder().CreateZExt(value, store_type); + } + + builder().CreateStore(value, addr); +} + auto FunctionContext::CopyValue(TypeInFile type, SemIR::InstId source_id, SemIR::InstId dest_id) -> void { switch (GetValueRepr(type).repr.kind) { @@ -326,7 +371,7 @@ auto FunctionContext::CopyValue(TypeInFile type, SemIR::InstId source_id, case SemIR::ValueRepr::None: break; case SemIR::ValueRepr::Copy: - builder().CreateStore(GetValue(source_id), GetValue(dest_id)); + StoreObject(type, GetValue(source_id), GetValue(dest_id)); break; case SemIR::ValueRepr::Pointer: CopyObject(type, source_id, dest_id); diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 5009ebc7d62f..ea8e3da1b670 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -62,6 +62,10 @@ class FunctionContext { struct TypeInFile { const SemIR::File* file; SemIR::TypeId type_id; + + auto GetPointeeType() -> TypeInFile { + return {.file = file, .type_id = file->GetPointeeType(type_id)}; + } }; // A value representation in a particular file. By convention, this represents @@ -194,6 +198,14 @@ class FunctionContext { // Returns the debug location to associate with the specified instruction. auto GetDebugLoc(SemIR::InstId inst_id) -> llvm::DebugLoc; + // Emits a load of an object representation of type `type`. + auto LoadObject(TypeInFile type, llvm::Value* addr, llvm::Twine name = "") + -> llvm::Value*; + + // Emits a store of an object representation of type `type`. + auto StoreObject(TypeInFile type, llvm::Value* value, llvm::Value* addr) + -> void; + // After emitting an initializer `init_id`, finishes performing the // initialization of `dest_id` from that initializer. This is a no-op if the // initialization was performed in-place, and otherwise performs a store or a diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index e36b933c45cb..bb307aad9f2f 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -103,8 +103,8 @@ static auto GetAggregateElement(FunctionContext& context, // `elem_ptr` points to a value representation. Load it. auto result_type = context.GetTypeIdOfInst(result_inst_id); auto result_value_type = context.GetValueRepr(result_type).type(); - return context.builder().CreateLoad( - context.GetType(result_value_type), elem_ptr, name + ".load"); + return context.LoadObject(result_value_type, elem_ptr, + name + ".load"); } case SemIR::ValueRepr::Custom: CARBON_FATAL( @@ -265,10 +265,11 @@ static auto EmitAggregateValueRepr(FunctionContext& context, // Write the value representation to a local alloca so we can produce a // pointer to it as the value representation of the struct or tuple. auto* alloca = context.builder().CreateAlloca(llvm_value_rep_type); - for (auto [i, ref] : + for (auto [i, ref_id] : llvm::enumerate(context.sem_ir().inst_blocks().Get(refs_id))) { - context.builder().CreateStore( - context.GetValue(ref), + context.StoreObject( + context.GetValueRepr(context.GetTypeIdOfInst(ref_id)).type(), + context.GetValue(ref_id), context.builder().CreateStructGEP(llvm_value_rep_type, alloca, i)); } return alloca; diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 2e525a32ccc3..34ac95a5735c 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -409,17 +409,13 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::FloatMulAssign: case SemIR::BuiltinFunctionKind::FloatDivAssign: { auto* lhs_ptr = context.GetValue(arg_ids[0]); - auto [lhs_type_file, lhs_type_id] = context.GetTypeIdOfInst(arg_ids[0]); - auto pointee_type_id = lhs_type_file->GetPointeeType(lhs_type_id); - // TODO: Factor out the code to create loads and stores, and include alias - // and alignment information. - auto* lhs_value = context.builder().CreateLoad( - context.GetFileContext(lhs_type_file).GetType(pointee_type_id), - lhs_ptr); + auto lhs_type = context.GetTypeIdOfInst(arg_ids[0]); + auto pointee_type = lhs_type.GetPointeeType(); + auto* lhs_value = context.LoadObject(pointee_type, lhs_ptr); auto* result = CreateBinaryOperatorForBuiltin( context, inst_id, builtin_kind, lhs_value, context.GetValue(arg_ids[1])); - context.builder().CreateStore(result, lhs_ptr); + context.StoreObject(pointee_type, result, lhs_ptr); // TODO: Add a helper to get a "no value representation" value. context.SetLocal(inst_id, llvm::PoisonValue::get(context.GetTypeOfInst(inst_id))); @@ -535,6 +531,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, // The vtable pointer is always at the start of the object in the Carbon // ABI, so a pointer to the object is a pointer to the vtable pointer - load // that to get a pointer to the vtable. + // TODO: Use `context.LoadObject`. auto* vtable = context.builder().CreateLoad(ptr_type, args.front(), "vtable"); auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); diff --git a/toolchain/lower/handle_expr_category.cpp b/toolchain/lower/handle_expr_category.cpp index 640ec6848db0..900585be16bc 100644 --- a/toolchain/lower/handle_expr_category.cpp +++ b/toolchain/lower/handle_expr_category.cpp @@ -22,11 +22,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, llvm::PoisonValue::get(context.GetType(inst_type))); break; - case SemIR::ValueRepr::Copy: { - auto* type = context.GetType(inst_type); - context.SetLocal(inst_id, context.builder().CreateLoad( - type, context.GetValue(inst.value_id))); - } break; + case SemIR::ValueRepr::Copy: + context.SetLocal( + inst_id, + context.LoadObject(inst_type, context.GetValue(inst.value_id))); + break; case SemIR::ValueRepr::Pointer: context.SetLocal(inst_id, context.GetValue(inst.value_id)); break; diff --git a/toolchain/lower/testdata/array/iterate.carbon b/toolchain/lower/testdata/array/iterate.carbon index efdb36a85f8a..a9ebbfcc8d4f 100644 --- a/toolchain/lower/testdata/array/iterate.carbon +++ b/toolchain/lower/testdata/array/iterate.carbon @@ -90,8 +90,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !25 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !27 -// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !27 -// CHECK:STDOUT: ret i1 %1, !dbg !28 +// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !27 +// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !27 +// CHECK:STDOUT: ret i1 %2, !dbg !28 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !29 { @@ -109,13 +110,13 @@ fn F() { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !37 // CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 1, !dbg !37 // CHECK:STDOUT: store i32 %value, ptr %value1, align 4, !dbg !37 -// CHECK:STDOUT: store i1 true, ptr %has_value, align 1, !dbg !37 +// CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !37 // CHECK:STDOUT: ret void, !dbg !38 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b88d1103f417c6d4(ptr sret({ i1, i32 }) %return) !dbg !39 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !40 -// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !40 +// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !40 // CHECK:STDOUT: ret void, !dbg !41 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/builtins/types.carbon b/toolchain/lower/testdata/builtins/types.carbon index b4432808397a..4432ccfb60ad 100644 --- a/toolchain/lower/testdata/builtins/types.carbon +++ b/toolchain/lower/testdata/builtins/types.carbon @@ -52,7 +52,7 @@ fn F() { // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %h.var), !dbg !11 // CHECK:STDOUT: store fp128 0xL00000000000000003FFF000000000000, ptr %h.var, align 16, !dbg !11 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !12 -// CHECK:STDOUT: store i1 false, ptr %b.var, align 1, !dbg !12 +// CHECK:STDOUT: store i8 0, ptr %b.var, align 1, !dbg !12 // CHECK:STDOUT: ret void, !dbg !13 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/class/generic.carbon b/toolchain/lower/testdata/class/generic.carbon index ec6f6e9df93c..371ab3d55f14 100644 --- a/toolchain/lower/testdata/class/generic.carbon +++ b/toolchain/lower/testdata/class/generic.carbon @@ -301,8 +301,9 @@ fn AccessTuple() -> (i32, i32, i32) { // CHECK:STDOUT: define linkonce_odr i1 @_CGetBool.C.Main.b88d1103f417c6d4(ptr %self) !dbg !27 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc6_16.1.v = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !28 -// CHECK:STDOUT: %.loc6_16.2 = load i1, ptr %.loc6_16.1.v, align 1, !dbg !28 -// CHECK:STDOUT: ret i1 %.loc6_16.2, !dbg !29 +// CHECK:STDOUT: %.loc6_16.2 = load i8, ptr %.loc6_16.1.v, align 1, !dbg !28 +// CHECK:STDOUT: %.loc6_16.21 = trunc i8 %.loc6_16.2 to i1, !dbg !28 +// CHECK:STDOUT: ret i1 %.loc6_16.21, !dbg !29 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i32 @_CGetT.C.Main.b88d1103f417c6d4(ptr %self) !dbg !30 { diff --git a/toolchain/lower/testdata/for/bindings.carbon b/toolchain/lower/testdata/for/bindings.carbon index 4e5310b5e721..fd151b43626b 100644 --- a/toolchain/lower/testdata/for/bindings.carbon +++ b/toolchain/lower/testdata/for/bindings.carbon @@ -92,8 +92,9 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.29d34654e802e24e(ptr %self) !dbg !18 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, { i32, i32 } }, ptr %self, i32 0, i32 0, !dbg !20 -// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !20 -// CHECK:STDOUT: ret i1 %1, !dbg !21 +// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !20 +// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !20 +// CHECK:STDOUT: ret i1 %2, !dbg !21 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CGet.Optional.Core.29d34654e802e24e(ptr sret({ i32, i32 }) %return, ptr %self) !dbg !22 { @@ -103,7 +104,7 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.29d34654e802e24e(ptr sret({ i1, { i32, i32 } }) %return) !dbg !25 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, { i32, i32 } }, ptr %return, i32 0, i32 0, !dbg !26 -// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !26 +// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !26 // CHECK:STDOUT: ret void, !dbg !27 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/for/break_continue.carbon b/toolchain/lower/testdata/for/break_continue.carbon index 012ac2957beb..7094c8bbd8af 100644 --- a/toolchain/lower/testdata/for/break_continue.carbon +++ b/toolchain/lower/testdata/for/break_continue.carbon @@ -109,8 +109,9 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !34 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !36 -// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !36 -// CHECK:STDOUT: ret i1 %1, !dbg !37 +// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !36 +// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !36 +// CHECK:STDOUT: ret i1 %2, !dbg !37 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !38 { @@ -130,13 +131,13 @@ fn For() { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !46 // CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 1, !dbg !46 // CHECK:STDOUT: store i32 %value, ptr %value1, align 4, !dbg !46 -// CHECK:STDOUT: store i1 true, ptr %has_value, align 1, !dbg !46 +// CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !46 // CHECK:STDOUT: ret void, !dbg !47 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b88d1103f417c6d4(ptr sret({ i1, i32 }) %return) !dbg !48 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !49 -// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !49 +// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !49 // CHECK:STDOUT: ret void, !dbg !50 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/for/for.carbon b/toolchain/lower/testdata/for/for.carbon index 5fc178485787..7645570dd9fb 100644 --- a/toolchain/lower/testdata/for/for.carbon +++ b/toolchain/lower/testdata/for/for.carbon @@ -97,8 +97,9 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !30 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !32 -// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !32 -// CHECK:STDOUT: ret i1 %1, !dbg !33 +// CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !32 +// CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !32 +// CHECK:STDOUT: ret i1 %2, !dbg !33 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !34 { @@ -118,13 +119,13 @@ fn For() { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !42 // CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 1, !dbg !42 // CHECK:STDOUT: store i32 %value, ptr %value1, align 4, !dbg !42 -// CHECK:STDOUT: store i1 true, ptr %has_value, align 1, !dbg !42 +// CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !42 // CHECK:STDOUT: ret void, !dbg !43 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b88d1103f417c6d4(ptr sret({ i1, i32 }) %return) !dbg !44 { // CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !45 -// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !45 +// CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !45 // CHECK:STDOUT: ret void, !dbg !46 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/function/generic/call_recursive_reorder_more.carbon b/toolchain/lower/testdata/function/generic/call_recursive_reorder_more.carbon index 5b5b48dfe430..05d98eb9a9b1 100644 --- a/toolchain/lower/testdata/function/generic/call_recursive_reorder_more.carbon +++ b/toolchain/lower/testdata/function/generic/call_recursive_reorder_more.carbon @@ -83,14 +83,16 @@ fn M() { // CHECK:STDOUT: %.loc39_14 = load ptr, ptr %ptr_bool.var, align 8, !dbg !21 // CHECK:STDOUT: %.loc39_24 = load ptr, ptr %ptr_i64.var, align 8, !dbg !22 // CHECK:STDOUT: %F.call.loc39 = call i32 @_CF.Main.9a89ac9e10448feb(i32 %.loc39_5, ptr %.loc39_14, ptr %.loc39_24, i32 0), !dbg !23 -// CHECK:STDOUT: %.loc40_5 = load i1, ptr %val_bool.var, align 1, !dbg !24 +// CHECK:STDOUT: %.loc40_5 = load i8, ptr %val_bool.var, align 1, !dbg !24 +// CHECK:STDOUT: %.loc40_51 = trunc i8 %.loc40_5 to i1, !dbg !24 // CHECK:STDOUT: %.loc40_15 = load ptr, ptr %ptr_bool.var, align 8, !dbg !25 // CHECK:STDOUT: %.loc40_25 = load ptr, ptr %ptr_i16.var, align 8, !dbg !26 -// CHECK:STDOUT: %F.call.loc40 = call i32 @_CF.Main.76a945443862cc1f(i1 %.loc40_5, ptr %.loc40_15, ptr %.loc40_25, i32 0), !dbg !27 +// CHECK:STDOUT: %F.call.loc40 = call i32 @_CF.Main.76a945443862cc1f(i1 %.loc40_51, ptr %.loc40_15, ptr %.loc40_25, i32 0), !dbg !27 // CHECK:STDOUT: %.loc41_5 = load i64, ptr %val_i64.var, align 4, !dbg !28 -// CHECK:STDOUT: %.loc41_14 = load i1, ptr %val_bool.var, align 1, !dbg !29 +// CHECK:STDOUT: %.loc41_14 = load i8, ptr %val_bool.var, align 1, !dbg !29 +// CHECK:STDOUT: %.loc41_142 = trunc i8 %.loc41_14 to i1, !dbg !29 // CHECK:STDOUT: %.loc41_24 = load ptr, ptr %ptr_bool.var, align 8, !dbg !30 -// CHECK:STDOUT: %F.call.loc41 = call i32 @_CF.Main.02da1a7614ea56b6(i64 %.loc41_5, i1 %.loc41_14, ptr %.loc41_24, i32 0), !dbg !31 +// CHECK:STDOUT: %F.call.loc41 = call i32 @_CF.Main.02da1a7614ea56b6(i64 %.loc41_5, i1 %.loc41_142, ptr %.loc41_24, i32 0), !dbg !31 // CHECK:STDOUT: %.loc42_5 = load double, ptr %val_f64.var, align 8, !dbg !32 // CHECK:STDOUT: %.loc42_14 = load ptr, ptr %ptr_bool.var, align 8, !dbg !33 // CHECK:STDOUT: %.loc42_24 = load ptr, ptr %ptr_bool.var, align 8, !dbg !34 diff --git a/toolchain/lower/testdata/operators/overloaded.carbon b/toolchain/lower/testdata/operators/overloaded.carbon index 586b9b1cfce2..af0c4c66e2df 100644 --- a/toolchain/lower/testdata/operators/overloaded.carbon +++ b/toolchain/lower/testdata/operators/overloaded.carbon @@ -36,39 +36,45 @@ fn Calculate(a: Number, b: Number) -> Number { // CHECK:STDOUT: define void @"_COp.Number.Main:Negate.Core"(ptr sret({ i1 }) %return, ptr %self) !dbg !4 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc19_36.1.is_positive = getelementptr inbounds nuw { i1 }, ptr %self, i32 0, i32 0, !dbg !7 -// CHECK:STDOUT: %.loc19_36.2 = load i1, ptr %.loc19_36.1.is_positive, align 1, !dbg !7 -// CHECK:STDOUT: %.loc19_28 = xor i1 %.loc19_36.2, true, !dbg !8 +// CHECK:STDOUT: %.loc19_36.2 = load i8, ptr %.loc19_36.1.is_positive, align 1, !dbg !7 +// CHECK:STDOUT: %.loc19_36.21 = trunc i8 %.loc19_36.2 to i1, !dbg !7 +// CHECK:STDOUT: %.loc19_28 = xor i1 %.loc19_36.21, true, !dbg !8 // CHECK:STDOUT: %.loc19_48.2.is_positive = getelementptr inbounds nuw { i1 }, ptr %return, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: store i1 %.loc19_28, ptr %.loc19_48.2.is_positive, align 1, !dbg !9 +// CHECK:STDOUT: %.loc19_48.3 = zext i1 %.loc19_28 to i8, !dbg !9 +// CHECK:STDOUT: store i8 %.loc19_48.3, ptr %.loc19_48.2.is_positive, align 1, !dbg !9 // CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @"_COp.Number.Main:MulWith.Core"(ptr sret({ i1 }) %return, ptr %self, ptr %other) !dbg !11 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc24_33.1.is_positive = getelementptr inbounds nuw { i1 }, ptr %self, i32 0, i32 0, !dbg !12 -// CHECK:STDOUT: %.loc24_33.2 = load i1, ptr %.loc24_33.1.is_positive, align 1, !dbg !12 -// CHECK:STDOUT: br i1 %.loc24_33.2, label %and.rhs.loc24, label %and.result.loc24, !dbg !12 +// CHECK:STDOUT: %.loc24_33.2 = load i8, ptr %.loc24_33.1.is_positive, align 1, !dbg !12 +// CHECK:STDOUT: %.loc24_33.21 = trunc i8 %.loc24_33.2 to i1, !dbg !12 +// CHECK:STDOUT: br i1 %.loc24_33.21, label %and.rhs.loc24, label %and.result.loc24, !dbg !12 // CHECK:STDOUT: // CHECK:STDOUT: and.rhs.loc24: ; preds = %entry // CHECK:STDOUT: %.loc24_55.1.is_positive = getelementptr inbounds nuw { i1 }, ptr %other, i32 0, i32 0, !dbg !13 -// CHECK:STDOUT: %.loc24_55.2 = load i1, ptr %.loc24_55.1.is_positive, align 1, !dbg !13 +// CHECK:STDOUT: %.loc24_55.2 = load i8, ptr %.loc24_55.1.is_positive, align 1, !dbg !13 +// CHECK:STDOUT: %.loc24_55.22 = trunc i8 %.loc24_55.2 to i1, !dbg !13 // CHECK:STDOUT: br label %and.result.loc24, !dbg !12 // CHECK:STDOUT: // CHECK:STDOUT: and.result.loc24: ; preds = %and.rhs.loc24, %entry -// CHECK:STDOUT: %0 = phi i1 [ false, %entry ], [ %.loc24_55.2, %and.rhs.loc24 ] +// CHECK:STDOUT: %0 = phi i1 [ false, %entry ], [ %.loc24_55.22, %and.rhs.loc24 ] // CHECK:STDOUT: %.loc24_69.1 = xor i1 %0, true, !dbg !14 // CHECK:STDOUT: br i1 %.loc24_69.1, label %or.rhs, label %or.result, !dbg !14 // CHECK:STDOUT: // CHECK:STDOUT: or.rhs: ; preds = %and.result.loc24 // CHECK:STDOUT: %.loc25_38.1.is_positive = getelementptr inbounds nuw { i1 }, ptr %self, i32 0, i32 0, !dbg !15 -// CHECK:STDOUT: %.loc25_38.2 = load i1, ptr %.loc25_38.1.is_positive, align 1, !dbg !15 -// CHECK:STDOUT: %.loc25_30 = xor i1 %.loc25_38.2, true, !dbg !16 +// CHECK:STDOUT: %.loc25_38.2 = load i8, ptr %.loc25_38.1.is_positive, align 1, !dbg !15 +// CHECK:STDOUT: %.loc25_38.23 = trunc i8 %.loc25_38.2 to i1, !dbg !15 +// CHECK:STDOUT: %.loc25_30 = xor i1 %.loc25_38.23, true, !dbg !16 // CHECK:STDOUT: br i1 %.loc25_30, label %and.rhs.loc25, label %and.result.loc25, !dbg !16 // CHECK:STDOUT: // CHECK:STDOUT: and.rhs.loc25: ; preds = %or.rhs // CHECK:STDOUT: %.loc25_64.1.is_positive = getelementptr inbounds nuw { i1 }, ptr %other, i32 0, i32 0, !dbg !17 -// CHECK:STDOUT: %.loc25_64.2 = load i1, ptr %.loc25_64.1.is_positive, align 1, !dbg !17 -// CHECK:STDOUT: %.loc25_55 = xor i1 %.loc25_64.2, true, !dbg !18 +// CHECK:STDOUT: %.loc25_64.2 = load i8, ptr %.loc25_64.1.is_positive, align 1, !dbg !17 +// CHECK:STDOUT: %.loc25_64.24 = trunc i8 %.loc25_64.2 to i1, !dbg !17 +// CHECK:STDOUT: %.loc25_55 = xor i1 %.loc25_64.24, true, !dbg !18 // CHECK:STDOUT: br label %and.result.loc25, !dbg !16 // CHECK:STDOUT: // CHECK:STDOUT: and.result.loc25: ; preds = %and.rhs.loc25, %or.rhs @@ -78,7 +84,8 @@ fn Calculate(a: Number, b: Number) -> Number { // CHECK:STDOUT: or.result: ; preds = %and.result.loc25, %and.result.loc24 // CHECK:STDOUT: %2 = phi i1 [ true, %and.result.loc24 ], [ %1, %and.result.loc25 ] // CHECK:STDOUT: %.loc25_77.2.is_positive = getelementptr inbounds nuw { i1 }, ptr %return, i32 0, i32 0, !dbg !19 -// CHECK:STDOUT: store i1 %2, ptr %.loc25_77.2.is_positive, align 1, !dbg !19 +// CHECK:STDOUT: %.loc25_77.3 = zext i1 %2 to i8, !dbg !19 +// CHECK:STDOUT: store i8 %.loc25_77.3, ptr %.loc25_77.2.is_positive, align 1, !dbg !19 // CHECK:STDOUT: ret void, !dbg !20 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/primitives/false_true.carbon b/toolchain/lower/testdata/primitives/bool.carbon similarity index 59% rename from toolchain/lower/testdata/primitives/false_true.carbon rename to toolchain/lower/testdata/primitives/bool.carbon index a98e0742a0ba..e742078ab5aa 100644 --- a/toolchain/lower/testdata/primitives/false_true.carbon +++ b/toolchain/lower/testdata/primitives/bool.carbon @@ -6,9 +6,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/false_true.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/bool.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/false_true.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/bool.carbon fn F() -> bool { return false; @@ -18,8 +18,14 @@ fn T() -> bool { return true; } -// CHECK:STDOUT: ; ModuleID = 'false_true.carbon' -// CHECK:STDOUT: source_filename = "false_true.carbon" +fn LoadStore(p: bool*, b: bool) -> bool { + let v: bool = *p; + *p = b; + return v; +} + +// CHECK:STDOUT: ; ModuleID = 'bool.carbon' +// CHECK:STDOUT: source_filename = "bool.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define i1 @_CF.Main() !dbg !4 { // CHECK:STDOUT: entry: @@ -31,16 +37,29 @@ fn T() -> bool { // CHECK:STDOUT: ret i1 true, !dbg !9 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CLoadStore.Main(ptr %p, i1 %b) !dbg !10 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc22_17.2 = load i8, ptr %p, align 1, !dbg !11 +// CHECK:STDOUT: %.loc22_17.21 = trunc i8 %.loc22_17.2 to i1, !dbg !11 +// CHECK:STDOUT: %0 = zext i1 %b to i8, !dbg !12 +// CHECK:STDOUT: store i8 %0, ptr %p, align 1, !dbg !12 +// CHECK:STDOUT: ret i1 %.loc22_17.21, !dbg !13 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} // CHECK:STDOUT: // CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) -// CHECK:STDOUT: !3 = !DIFile(filename: "false_true.carbon", directory: "") +// CHECK:STDOUT: !3 = !DIFile(filename: "bool.carbon", directory: "") // CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{} // CHECK:STDOUT: !7 = !DILocation(line: 14, column: 3, scope: !4) // CHECK:STDOUT: !8 = distinct !DISubprogram(name: "T", linkageName: "_CT.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !9 = !DILocation(line: 18, column: 3, scope: !8) +// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "LoadStore", linkageName: "_CLoadStore.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !11 = !DILocation(line: 22, column: 17, scope: !10) +// CHECK:STDOUT: !12 = !DILocation(line: 23, column: 3, scope: !10) +// CHECK:STDOUT: !13 = !DILocation(line: 24, column: 3, scope: !10) diff --git a/toolchain/lower/testdata/primitives/int_types.carbon b/toolchain/lower/testdata/primitives/int_types.carbon index 86a64851f0d5..b1e3a83e890c 100644 --- a/toolchain/lower/testdata/primitives/int_types.carbon +++ b/toolchain/lower/testdata/primitives/int_types.carbon @@ -15,6 +15,18 @@ fn F_u16(a: u16) -> u16 { return a; } fn F_i64(a: i64) -> i64 { return a; } fn F_u65536(a: u65536) -> u65536 { return a; } +fn LoadStore_Bytes(p: Core.Int(24)*, n: Core.Int(24)) -> Core.Int(24) { + let v: Core.Int(24) = *p; + *p = n; + return v; +} + +fn LoadStore_NotBytes(p: Core.Int(13)*, n: Core.Int(13)) -> Core.Int(13) { + let v: Core.Int(13) = *p; + *p = n; + return v; +} + // CHECK:STDOUT: ; ModuleID = 'int_types.carbon' // CHECK:STDOUT: source_filename = "int_types.carbon" // CHECK:STDOUT: @@ -38,6 +50,22 @@ fn F_u65536(a: u65536) -> u65536 { return a; } // CHECK:STDOUT: ret i65536 %a, !dbg !13 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: define i24 @_CLoadStore_Bytes.Main(ptr %p, i24 %n) !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc19_25.2 = load i24, ptr %p, align 4, !dbg !15 +// CHECK:STDOUT: store i24 %n, ptr %p, align 4, !dbg !16 +// CHECK:STDOUT: ret i24 %.loc19_25.2, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i13 @_CLoadStore_NotBytes.Main(ptr %p, i13 %n) !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc25_25.2 = load i16, ptr %p, align 2, !dbg !19 +// CHECK:STDOUT: %.loc25_25.21 = trunc i16 %.loc25_25.2 to i13, !dbg !19 +// CHECK:STDOUT: %0 = zext i13 %n to i16, !dbg !20 +// CHECK:STDOUT: store i16 %0, ptr %p, align 2, !dbg !20 +// CHECK:STDOUT: ret i13 %.loc25_25.21, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} // CHECK:STDOUT: @@ -55,3 +83,11 @@ fn F_u65536(a: u65536) -> u65536 { return a; } // CHECK:STDOUT: !11 = !DILocation(line: 15, column: 27, scope: !10) // CHECK:STDOUT: !12 = distinct !DISubprogram(name: "F_u65536", linkageName: "_CF_u65536.Main", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !13 = !DILocation(line: 16, column: 36, scope: !12) +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "LoadStore_Bytes", linkageName: "_CLoadStore_Bytes.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !15 = !DILocation(line: 19, column: 25, scope: !14) +// CHECK:STDOUT: !16 = !DILocation(line: 20, column: 3, scope: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 21, column: 3, scope: !14) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "LoadStore_NotBytes", linkageName: "_CLoadStore_NotBytes.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 25, column: 25, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 26, column: 3, scope: !18) +// CHECK:STDOUT: !21 = !DILocation(line: 27, column: 3, scope: !18)