From 4e39d561d5daab3241999ccdc9c496facc5b4070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= Date: Wed, 5 Feb 2025 10:13:07 +0100 Subject: [PATCH] rt: Improve support for new default method implementations --- dora-runtime/src/boots/serializer.rs | 6 ++++++ dora-runtime/src/cannon/codegen.rs | 16 +++++++++++++--- dora-runtime/src/compiler/aot.rs | 4 ++-- pkgs/boots/bytecode/data.dora | 6 ++++-- pkgs/boots/compilation.dora | 3 +++ pkgs/boots/deserializer.dora | 17 ++++++++++++++++- pkgs/boots/graph_builder.dora | 19 +++++++++++++++---- pkgs/boots/location.dora | 5 +++-- pkgs/boots/specialize.dora | 6 +++--- 9 files changed, 65 insertions(+), 17 deletions(-) diff --git a/dora-runtime/src/boots/serializer.rs b/dora-runtime/src/boots/serializer.rs index 97b7056d3..ebc6431cb 100644 --- a/dora-runtime/src/boots/serializer.rs +++ b/dora-runtime/src/boots/serializer.rs @@ -79,6 +79,12 @@ fn encode_compilation_info( buffer.emit_id(compilation_data.fct_id.0 as usize); encode_type_params(vm, &compilation_data.type_params, buffer); encode_bytecode_type(vm, &compilation_data.return_type, buffer); + if let Some(ref specialize_self) = compilation_data.specialize_self { + buffer.emit_bool(true); + encode_bytecode_type(vm, specialize_self, buffer); + } else { + buffer.emit_bool(false); + } encode_location(&compilation_data.loc, buffer); buffer.emit_u8(mode as u8); buffer.emit_bool(compilation_data.emit_debug); diff --git a/dora-runtime/src/cannon/codegen.rs b/dora-runtime/src/cannon/codegen.rs index 458e7f60a..c09686cee 100644 --- a/dora-runtime/src/cannon/codegen.rs +++ b/dora-runtime/src/cannon/codegen.rs @@ -2634,11 +2634,22 @@ impl<'a> CannonCodeGen<'a> { } fn emit_invoke_generic(&mut self, dest: Register, fct_idx: ConstPoolIdx, is_static: bool) { - let (id, trait_fct_id, type_params) = match self.bytecode.const_pool(fct_idx) { - ConstPoolEntry::Generic(id, fct_id, type_params) => (*id, *fct_id, type_params.clone()), + let (ty, trait_fct_id, type_params) = match self.bytecode.const_pool(fct_idx) { + ConstPoolEntry::Generic(id, fct_id, type_params) => ( + self.type_params[*id as usize].clone(), + *fct_id, + type_params.clone(), + ), + ConstPoolEntry::GenericSelf(fct_id, type_params) => ( + self.specialize_self.clone().expect("missing Self type"), + *fct_id, + type_params.clone(), + ), _ => unreachable!(), }; + assert!(ty.is_concrete_type()); + let fct = self.vm.fct(trait_fct_id); let trait_id = match fct.kind { FunctionKind::Trait(trait_id) => trait_id, @@ -2650,7 +2661,6 @@ impl<'a> CannonCodeGen<'a> { bindings: Vec::new(), }; - let ty = self.type_params[id as usize].clone(); let (callee_id, type_params) = find_trait_impl(self.vm, trait_fct_id, trait_ty, ty); let pos = self.bytecode.offset_location(self.current_offset.to_u32()); diff --git a/dora-runtime/src/compiler/aot.rs b/dora-runtime/src/compiler/aot.rs index 3604d000e..0f53370d1 100644 --- a/dora-runtime/src/compiler/aot.rs +++ b/dora-runtime/src/compiler/aot.rs @@ -505,7 +505,7 @@ fn compile_function( .insert((fct_id, type_params), code.instruction_start()); assert!(existing.is_none()); ctc.code_objects.push(code); - } else if let Some(_) = fct.bytecode { + } else if let Some(_) = get_bytecode(vm, fct) { let (_code_id, code) = compile_fct_aot(vm, fct_id, &type_params, compiler, mode); ctc.counter += 1; let existing = ctc @@ -569,7 +569,7 @@ fn prepare_lazy_call_sites( println!( "code = {:?} {}", code.descriptor(), - display_fct(&_vm.program, *fct_id) + display_fct(&_vm.program, code.fct_id()) ); println!( " calls {} with {:?}", diff --git a/pkgs/boots/bytecode/data.dora b/pkgs/boots/bytecode/data.dora index f37a26381..ce344425e 100644 --- a/pkgs/boots/bytecode/data.dora +++ b/pkgs/boots/bytecode/data.dora @@ -79,6 +79,7 @@ pub enum ConstPoolEntry { Fct(FunctionId, Array[BytecodeType]), TraitObjectMethod(BytecodeType, FunctionId), Generic(Int32, FunctionId, Array[BytecodeType]), + GenericSelf(FunctionId, Array[BytecodeType]), Enum(EnumId, Array[BytecodeType]), EnumVariant(EnumId, Array[BytecodeType], Int32), EnumElement(EnumId, Array[BytecodeType], Int32, Int32), @@ -149,6 +150,7 @@ impl Stringable for ConstPoolEntry { ConstPoolEntry::Fct(id, typeParams) => "Fct ${id}${typeParams}", ConstPoolEntry::TraitObjectMethod(ty, id) => "TraitObjectMethod ${ty} ${id}", ConstPoolEntry::Generic(tp, id, typeParams) => "Generic ${tp} ${id}${typeParams}", + ConstPoolEntry::GenericSelf(id, typeParams) => "GenericSelf ${id}${typeParams}", ConstPoolEntry::Enum(id, typeParams) => "Enum ${id}${typeParams}", ConstPoolEntry::EnumVariant(id, typeParams, variant) => "EnumVariant ${id}${typeParams}::${variant}", ConstPoolEntry::EnumElement(id, typeParams, variant, element) => "EnumVariant ${id}${typeParams}::${variant}.${element}", @@ -249,8 +251,8 @@ impl BytecodeType { | BytecodeType::Int32 | BytecodeType::Int64 | BytecodeType::Float32 - | BytecodeType::Float64 - | BytecodeType::This => false, + | BytecodeType::Float64 => false, + BytecodeType::This => true, BytecodeType::Struct(struct_id, type_params) => type_params.isGeneric(), BytecodeType::Enum(enum_id, type_params) => type_params.isGeneric(), BytecodeType::Class(class_id, type_params) => type_params.isGeneric(), diff --git a/pkgs/boots/compilation.dora b/pkgs/boots/compilation.dora index b541dcbd4..38b70495e 100644 --- a/pkgs/boots/compilation.dora +++ b/pkgs/boots/compilation.dora @@ -7,6 +7,7 @@ pub class CompilationInfo { pub fctId: Option[FunctionId], pub typeParams: Array[BytecodeType], pub returnType: BytecodeType, + pub specializeSelf: Option[BytecodeType], pub loc: Location, pub compilationMode: iface::CompilationMode, pub emitDebug: Bool, @@ -26,6 +27,7 @@ impl CompilationInfo { id: FunctionId, typeParams: Array[BytecodeType], returnType: BytecodeType, + specializeSelf: Option[BytecodeType], loc: Location, compilationMode: iface::CompilationMode, emitDebug: Bool, @@ -38,6 +40,7 @@ impl CompilationInfo { fctId = Some[FunctionId](id), typeParams, returnType, + specializeSelf, loc, compilationMode, emitDebug, diff --git a/pkgs/boots/deserializer.dora b/pkgs/boots/deserializer.dora index 2b33b8020..50ea5a445 100644 --- a/pkgs/boots/deserializer.dora +++ b/pkgs/boots/deserializer.dora @@ -49,13 +49,14 @@ pub fn decodeCompilationInfo(reader: ByteReader): CompilationInfo { let fctId = FunctionId(reader.readInt32()); let typeParams = decodeTypeParams(reader); let returnType = decodeBytecodeType(reader); + let specializeSelf = decodeOptionBytecodeType(reader); let loc = decodeLocation(reader); let compilationMode = decodeCompilationMode(reader); let emitDebug = reader.readBool(); let emitGraph = reader.readBool(); let emitHtml = reader.readBool(); let emitCodeComments = reader.readBool(); - CompilationInfo::new(bc, fctId, typeParams, returnType, loc, compilationMode, emitDebug, emitGraph, emitHtml, emitCodeComments) + CompilationInfo::new(bc, fctId, typeParams, returnType, specializeSelf, loc, compilationMode, emitDebug, emitGraph, emitHtml, emitCodeComments) } pub fn decodeStructData(reader: ByteReader): StructData { @@ -132,6 +133,14 @@ fn decodeRegisters(reader: ByteReader): Array[BytecodeType] { data } +pub fn decodeOptionBytecodeType(reader: ByteReader): Option[BytecodeType] { + if reader.readBool() { + Some[BytecodeType](decodeBytecodeType(reader)) + } else { + None[BytecodeType] + } +} + pub fn decodeBytecodeType(reader: ByteReader): BytecodeType { let opcode = reader.readUInt8().toInt32(); @@ -188,6 +197,8 @@ pub fn decodeBytecodeType(reader: ByteReader): BytecodeType { let traitTy = decodeBytecodeTraitType(reader); let assocId = AliasId(reader.readId()); BytecodeType::GenericAssoc(typeParamId, traitTy, assocId) + } else if opcode == opc::BC_TYPE_THIS { + BytecodeType::This } else { println("unknown bytecode type opcode = ${opcode}"); unreachable[BytecodeType]() @@ -267,6 +278,10 @@ fn decodeConstPoolEntry(reader: ByteReader): ConstPoolEntry { let fct_id = FunctionId(reader.readInt32()); let type_params = decodeBytecodeTypeArray(reader); ConstPoolEntry::Generic(idx, fct_id, type_params) + } else if opcode == opc::CONSTPOOL_OPCODE_GENERIC_SELF { + let fct_id = FunctionId(reader.readInt32()); + let type_params = decodeBytecodeTypeArray(reader); + ConstPoolEntry::GenericSelf(fct_id, type_params) } else if opcode == opc::CONSTPOOL_OPCODE_ENUM { let enum_id = EnumId(reader.readInt32()); let type_params = decodeBytecodeTypeArray(reader); diff --git a/pkgs/boots/graph_builder.dora b/pkgs/boots/graph_builder.dora index 676aa345b..0cd39e99d 100644 --- a/pkgs/boots/graph_builder.dora +++ b/pkgs/boots/graph_builder.dora @@ -1464,8 +1464,20 @@ impl SsaGen { fn emitInvokeGeneric(dest: BytecodeRegister, idx: ConstPoolId, kind: CallKind): Bool { let ty = self.regGraphTy(dest); - let ConstPoolEntry::Generic(type_param_idx, trait_fct_id, trait_type_params) = self.bc.constPool(idx); - let object_ty = self.typeParams(type_param_idx.toInt64()); + let (object_ty, trait_fct_id, trait_type_params) = match self.bc.constPool(idx) { + ConstPoolEntry::Generic(type_param_idx, trait_fct_id, trait_type_params) => { + (self.typeParams(type_param_idx.toInt64()), trait_fct_id, trait_type_params) + } + + ConstPoolEntry::GenericSelf(trait_fct_id, trait_type_params) => { + (self.ci.specializeSelf.getOrPanic(), trait_fct_id, trait_type_params) + } + + _ => unreachable[(BytecodeType, FunctionId, Array[BytecodeType])](), + }; + + assert(!object_ty.isGeneric()); + let (callee_id, type_params) = iface::findTraitImpl(self.ci, trait_fct_id, trait_type_params, object_ty); let intrinsic = iface::getIntrinsicForFunction(callee_id); @@ -2725,8 +2737,7 @@ impl SsaGen { } BytecodeType::Struct(..) | BytecodeType::Tuple(_) => Type::Address, - BytecodeType::This - | BytecodeType::TypeParam(_) => unreachable[Type](), + BytecodeType::This | BytecodeType::TypeParam(_) => unreachable[Type](), BytecodeType::TypeAlias(..) | BytecodeType::Assoc(..) | BytecodeType::GenericAssoc(..) => unreachable[Type](), } } diff --git a/pkgs/boots/location.dora b/pkgs/boots/location.dora index fcc752110..6c6e87a97 100644 --- a/pkgs/boots/location.dora +++ b/pkgs/boots/location.dora @@ -4,6 +4,7 @@ use package::codegen::CodeGen; use package::compilation::CompilationInfo; use package::graph::{Graph, InputOutputOverlap, Inst, Location, LocationData, Op, Operand, Policy, StackSlot, Type}; use package::interface::{config, PTR_SIZE, Architecture}; +use package::specialize::specializeTy; pub fn ensureLocationData(graph: Graph, codegen: CodeGen, ci: CompilationInfo): Int32 { let argumentLocations = computeArgumentLocations(codegen, ci); @@ -471,7 +472,7 @@ fn computeArgumentLocations(codegen: CodeGen, ci: CompilationInfo): ArgumentLoca let gp_argument_registers = codegen.argumentRegisters(); let fp_argument_registers = codegen.argumentFloatRegisters(); - let retTy = ci.returnType.specialize(ci.typeParams); + let retTy = specializeTy(ci, ci.returnType, ci.typeParams); if retTy.isStruct() || retTy.isTuple() { locations.push(gp_argument_registers(gp_idx).toLocation()); @@ -480,7 +481,7 @@ fn computeArgumentLocations(codegen: CodeGen, ci: CompilationInfo): ArgumentLoca for idx in std::range(0, arguments) { let ty = ci.bc.registers(idx); - let ty = ty.specialize(ci.typeParams); + let ty = specializeTy(ci, ty, ci.typeParams); if ty.isUnit() { continue; diff --git a/pkgs/boots/specialize.dora b/pkgs/boots/specialize.dora index 73b9426ef..8116d7e1e 100644 --- a/pkgs/boots/specialize.dora +++ b/pkgs/boots/specialize.dora @@ -3,7 +3,7 @@ use package::interface as iface; use package::CompilationInfo; pub fn specializeTy(ci: CompilationInfo, ty: BytecodeType, type_params: Array[BytecodeType]): BytecodeType { - if type_params.isEmpty() || !ty.isGeneric() { + if !ty.isGeneric() { return ty; } @@ -16,8 +16,8 @@ pub fn specializeTy(ci: CompilationInfo, ty: BytecodeType, type_params: Array[By | BytecodeType::Int32 | BytecodeType::Int64 | BytecodeType::Float32 - | BytecodeType::Float64 - | BytecodeType::This => ty, + | BytecodeType::Float64 => ty, + BytecodeType::This => ci.specializeSelf.getOrPanic(), BytecodeType::Struct(struct_id, struct_type_params) => { BytecodeType::Struct(struct_id, specializeArray(ci, struct_type_params, type_params)) },