From 34b460757b33d9bc2c518873beb9c0443d730465 Mon Sep 17 00:00:00 2001 From: ychenfo Date: Tue, 15 Mar 2022 03:27:12 +0800 Subject: [PATCH] nac3core: option type codegen support --- nac3core/src/codegen/expr.rs | 17 +++++++++++ nac3core/src/codegen/mod.rs | 38 +++++++++++++++++++----- nac3core/src/toplevel/builtins.rs | 49 +++++++++++++++++++++++++------ nac3core/src/toplevel/helper.rs | 1 + 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 5afeba8c..3d4d75ad 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -138,6 +138,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { &mut self.unifier, self.top_level, &mut self.type_cache, + &self.primitives, ty, ) } @@ -199,6 +200,22 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { val } } + Constant::None => { + match ( + self.unifier.get_ty(ty).as_ref(), + self.unifier.get_ty(self.primitives.option).as_ref(), + ) { + ( + TypeEnum::TObj { obj_id, params, .. }, + TypeEnum::TObj { obj_id: opt_id, .. }, + ) if *obj_id == *opt_id => self + .get_llvm_type(generator, *params.iter().next().unwrap().1) + .ptr_type(AddressSpace::Generic) + .const_null() + .into(), + _ => unreachable!("must be option type"), + } + } _ => unreachable!(), } } diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 6549b743..78c866d4 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -259,6 +259,7 @@ fn get_llvm_type<'ctx>( unifier: &mut Unifier, top_level: &TopLevelContext, type_cache: &mut HashMap>, + primitives: &PrimitiveStore, ty: Type, ) -> BasicTypeEnum<'ctx> { use TypeEnum::*; @@ -268,9 +269,28 @@ fn get_llvm_type<'ctx>( let ty_enum = unifier.get_ty(ty); let result = match &*ty_enum { TObj { obj_id, fields, .. } => { - // check to avoid treating primitives as classes - if obj_id.0 <= 7 { - unreachable!(); + // check to avoid treating primitives other than Option as classes + if obj_id.0 <= 14 { + match (unifier.get_ty(ty).as_ref(), unifier.get_ty(primitives.option).as_ref()) + { + ( + TypeEnum::TObj { obj_id, params, .. }, + TypeEnum::TObj { obj_id: opt_id, .. }, + ) if *obj_id == *opt_id => { + return get_llvm_type( + ctx, + generator, + unifier, + top_level, + type_cache, + primitives, + *params.iter().next().unwrap().1, + ) + .ptr_type(AddressSpace::Generic) + .into(); + } + _ => unreachable!("must be option type"), + } } // a struct with fields in the order of declaration let top_level_defs = top_level.definitions.read(); @@ -289,6 +309,7 @@ fn get_llvm_type<'ctx>( unifier, top_level, type_cache, + primitives, fields[&f.0].0, ) }) @@ -304,14 +325,14 @@ fn get_llvm_type<'ctx>( // a struct with fields in the order present in the tuple let fields = ty .iter() - .map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty)) + .map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, primitives, *ty)) .collect_vec(); ctx.struct_type(&fields, false).into() } TList { ty } => { // a struct with an integer and a pointer to an array let element_type = - get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty); + get_llvm_type(ctx, generator, unifier, top_level, type_cache, primitives, *ty); let fields = [ element_type.ptr_type(AddressSpace::Generic).into(), generator.get_size_type(ctx).into(), @@ -418,7 +439,8 @@ pub fn gen_func<'ctx, G: CodeGenerator>( exception.set_body(&fields, false); exception.ptr_type(AddressSpace::Generic).into() }); - // TODO: insert option type cache + // NOTE: special handling of option cannot use this type cache since it contains type var, + // handled inside get_llvm_type instead let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } = task.store.get(task.signature) @@ -439,7 +461,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>( let ret_type = if unifier.unioned(ret, primitives.none) { None } else { - Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, ret)) + Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret)) }; let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty)); @@ -452,6 +474,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>( &mut unifier, top_level_ctx.as_ref(), &mut type_cache, + &primitives, arg.ty, ) .into() @@ -499,6 +522,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>( &mut unifier, top_level_ctx.as_ref(), &mut type_cache, + &primitives, arg.ty, ), &arg.name.to_string(), diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 9c2b6e36..0367b574 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -105,7 +105,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { ("__param2__".into(), int64, true), ]; - // for option type + // for Option, is_some and is_none share the same type let (is_some_ty, unwrap_ty, (option_ty_var, option_ty_var_id)) = if let TypeEnum::TObj { fields, params, .. } = primitives.1.get_ty(primitives.0.option).as_ref() @@ -222,9 +222,33 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { instance_to_stmt: Default::default(), resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( - |ctx, _, fun, args, generator| { - // TODO: - unimplemented!() + |ctx, obj, _, _, generator| { + let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?; + if let BasicValueEnum::PointerValue(ptr) = obj_val { + Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into())) + } else { + unreachable!("option must be ptr") + } + }, + )))), + loc: None, + })), + Arc::new(RwLock::new(TopLevelDef::Function { + name: "Option.is_none".into(), + simple_name: "is_none".into(), + signature: is_some_ty.0, + var_id: vec![option_ty_var_id], + instance_to_symbol: Default::default(), + instance_to_stmt: Default::default(), + resolver: None, + codegen_callback: Some(Arc::new(GenCall::new(Box::new( + |ctx, obj, _, _, generator| { + let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?; + if let BasicValueEnum::PointerValue(ptr) = obj_val { + Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into())) + } else { + unreachable!("option must be ptr") + } }, )))), loc: None, @@ -238,9 +262,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { instance_to_stmt: Default::default(), resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( - |ctx, _, fun, args, generator| { - // TODO: - unimplemented!() + |ctx, obj, _, _, generator| { + let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?; + if let BasicValueEnum::PointerValue(ptr) = obj_val { + Ok(Some(ctx.builder.build_load(ptr, "unwrap_some"))) + } else { + unreachable!("option must be ptr") + } }, )))), loc: None, @@ -1176,8 +1204,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { instance_to_stmt: Default::default(), resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( - |ctx, _, fun, args, generator| { - unimplemented!() + |ctx, _, _fun, args, generator| { + let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?; + let alloca = ctx.builder.build_alloca(arg_val.get_type(), "alloca_some"); + ctx.builder.build_store(alloca, arg_val); + Ok(Some(alloca.into())) }, )))), loc: None, diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index 0c00344b..793bb927 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -123,6 +123,7 @@ impl TopLevelComposer { obj_id: DefinitionId(10), fields: vec![ ("is_some".into(), (is_some_type_fun_ty, true)), + ("is_none".into(), (is_some_type_fun_ty, true)), ("unwrap".into(), (unwrap_fun_ty, true)), ] .into_iter()