Skip to content

Commit

Permalink
Insert a value_of_initializer after a call to ImplicitAs where po…
Browse files Browse the repository at this point in the history
…ssible.

This avoids going through memory when performing an implicit conversion
to a type with a by-copy value representation, such as i32.
  • Loading branch information
zygoloid committed Nov 1, 2024
1 parent ac5cc33 commit cb3b589
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 26 deletions.
49 changes: 36 additions & 13 deletions toolchain/check/convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,32 @@ static auto IsValidExprCategoryForConversionTarget(
}
}

// Determines whether we can pull a value directly out of an initializing
// expression of type `type_id` to initialize a target of type `type_id` and
// kind `target_kind`.
static auto CanUseValueOfInitializer(const SemIR::File& sem_ir,
SemIR::TypeId type_id,
ConversionTarget::Kind target_kind)
-> bool {
if (!IsValidExprCategoryForConversionTarget(SemIR::ExprCategory::Value,
target_kind)) {
// We don't want a value expression.
return false;
}

if (SemIR::InitRepr::ForType(sem_ir, type_id).kind !=
SemIR::InitRepr::ByCopy) {
// The initializing expression doesn't contain a copy of a value.
return false;
}

// If the value representation is a copy of the object representation, we
// already have a value of the right form and can use that value directly.
auto value_rep = SemIR::ValueRepr::ForType(sem_ir, type_id);
return value_rep.kind == SemIR::ValueRepr::Copy &&
value_rep.type_id == type_id;
}

// Returns the non-adapter type that is compatible with the specified type.
static auto GetCompatibleBaseType(Context& context, SemIR::TypeId type_id)
-> SemIR::TypeId {
Expand Down Expand Up @@ -738,19 +764,9 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
// If the source is an initializing expression, we may be able to pull a
// value right out of it.
if (value_cat == SemIR::ExprCategory::Initializing &&
IsValidExprCategoryForConversionTarget(SemIR::ExprCategory::Value,
target.kind) &&
SemIR::InitRepr::ForType(sem_ir, value_type_id).kind ==
SemIR::InitRepr::ByCopy) {
auto value_rep = SemIR::ValueRepr::ForType(sem_ir, value_type_id);
if (value_rep.kind == SemIR::ValueRepr::Copy &&
value_rep.type_id == value_type_id) {
// The initializer produces an object representation by copy, and the
// value representation is a copy of the object representation, so we
// already have a value of the right form.
return context.AddInst<SemIR::ValueOfInitializer>(
loc_id, {.type_id = value_type_id, .init_id = value_id});
}
CanUseValueOfInitializer(sem_ir, value_type_id, target.kind)) {
return context.AddInst<SemIR::ValueOfInitializer>(
loc_id, {.type_id = value_type_id, .init_id = value_id});
}
}

Expand Down Expand Up @@ -998,6 +1014,13 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
: ImplicitAsConversionFailure,
expr_id, target.type_id);
});

// Pull a value directly out of the initializer if possible and wanted.
if (expr_id != SemIR::InstId::BuiltinError &&
CanUseValueOfInitializer(sem_ir, target.type_id, target.kind)) {
expr_id = context.AddInst<SemIR::ValueOfInitializer>(
loc_id, {.type_id = target.type_id, .init_id = expr_id});
}
}

// Track that we performed a type conversion, if we did so.
Expand Down
7 changes: 3 additions & 4 deletions toolchain/check/testdata/as/overloaded.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,9 @@ let n: i32 = ((4 as i32) as X) as i32;
// CHECK:STDOUT: %.loc23_32.3: <bound method> = bound_method %.loc23_26.6, %.loc23_32.2
// CHECK:STDOUT: %.loc23_26.7: %X = bind_value %.loc23_26.6
// CHECK:STDOUT: %Convert.call.loc23_32: init i32 = call %.loc23_32.3(%.loc23_26.7)
// CHECK:STDOUT: %.loc23_32.4: init i32 = converted %.loc23_26.5, %Convert.call.loc23_32
// CHECK:STDOUT: %.loc23_38.1: i32 = value_of_initializer %.loc23_32.4
// CHECK:STDOUT: %.loc23_38.2: i32 = converted %.loc23_32.4, %.loc23_38.1
// CHECK:STDOUT: %n: i32 = bind_name n, %.loc23_38.2
// CHECK:STDOUT: %.loc23_32.4: i32 = value_of_initializer %Convert.call.loc23_32
// CHECK:STDOUT: %.loc23_32.5: i32 = converted %.loc23_26.5, %.loc23_32.4
// CHECK:STDOUT: %n: i32 = bind_name n, %.loc23_32.5
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,9 @@ fn Test() {
// CHECK:STDOUT: %.loc30_18.5: <bound method> = bound_method %.loc30_18.3, %.loc30_18.4
// CHECK:STDOUT: %.loc30_18.6: %X = bind_value %.loc30_18.3
// CHECK:STDOUT: %Convert.call.loc30: init i32 = call %.loc30_18.5(%.loc30_18.6)
// CHECK:STDOUT: %.loc30_18.7: init i32 = converted %Source.call.loc30, %Convert.call.loc30
// CHECK:STDOUT: %.loc30_18.8: ref i32 = temporary_storage
// CHECK:STDOUT: %.loc30_18.9: ref i32 = temporary %.loc30_18.8, %.loc30_18.7
// CHECK:STDOUT: %.loc30_18.10: i32 = bind_value %.loc30_18.9
// CHECK:STDOUT: %Sink_i32.call: init %.1 = call %Sink_i32.ref(%.loc30_18.10)
// CHECK:STDOUT: %.loc30_18.7: i32 = value_of_initializer %Convert.call.loc30
// CHECK:STDOUT: %.loc30_18.8: i32 = converted %Source.call.loc30, %.loc30_18.7
// CHECK:STDOUT: %Sink_i32.call: init %.1 = call %Sink_i32.ref(%.loc30_18.8)
// CHECK:STDOUT: %Sink_X.ref: %Sink_X.type = name_ref Sink_X, file.%Sink_X.decl [template = constants.%Sink_X]
// CHECK:STDOUT: %Source.ref.loc31: %Source.type = name_ref Source, file.%Source.decl [template = constants.%Source]
// CHECK:STDOUT: %int.make_type_32: init type = call constants.%Int32() [template = i32]
Expand Down
5 changes: 1 addition & 4 deletions toolchain/lower/testdata/class/convert.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,7 @@ fn DoIt() {
// CHECK:STDOUT: %.loc22_31.2.n = getelementptr inbounds nuw { i32 }, ptr %w.var, i32 0, i32 0, !dbg !13
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %w.var, ptr align 4 @struct.loc22_32, i64 4, i1 false), !dbg !14
// CHECK:STDOUT: %Convert.call = call i32 @"_CConvert.IntWrapper.Main:ImplicitAs.Core"(ptr %w.var), !dbg !15
// CHECK:STDOUT: %.loc23_11.6.temp = alloca i32, align 4, !dbg !15
// CHECK:STDOUT: store i32 %Convert.call, ptr %.loc23_11.6.temp, align 4, !dbg !15
// CHECK:STDOUT: %.loc23_11.8 = load i32, ptr %.loc23_11.6.temp, align 4, !dbg !15
// CHECK:STDOUT: call void @_CConsume.Main(i32 %.loc23_11.8), !dbg !16
// CHECK:STDOUT: call void @_CConsume.Main(i32 %Convert.call), !dbg !16
// CHECK:STDOUT: ret void, !dbg !17
// CHECK:STDOUT: }
// CHECK:STDOUT:
Expand Down

0 comments on commit cb3b589

Please sign in to comment.