diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index d1b24eb..4e38bdc 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -450,7 +450,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>( let alloc_bb = ctx.ctx.append_basic_block(current_function, "rpc.continue"); let tail_bb = ctx.ctx.append_basic_block(current_function, "rpc.tail"); - let ret_ty = ctx.get_llvm_type(generator, fun.0.ret); + let ret_ty = ctx.get_llvm_abi_type(generator, fun.0.ret); let need_load = !ret_ty.is_pointer_type(); let slot = ctx.builder.build_alloca(ret_ty, "rpc.ret.slot"); let slotgen = ctx.builder.build_bitcast(slot, ptr_type, "rpc.ret.ptr"); diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index f8a3ae9..9953cba 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -134,9 +134,7 @@ impl StaticValue for PythonValue { PrimitiveValue::U32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(), PrimitiveValue::U64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(), PrimitiveValue::F64(val) => ctx.ctx.f64_type().const_float(*val).into(), - PrimitiveValue::Bool(val) => { - ctx.ctx.bool_type().const_int(*val as u64, false).into() - } + PrimitiveValue::Bool(val) => ctx.ctx.i8_type().const_int(*val as u64, false).into(), }); } if let Some(global) = ctx.module.get_global(&self.id.to_string()) { @@ -808,7 +806,7 @@ impl InnerResolver { } else if ty_id == self.primitive_ids.bool { let val: bool = obj.extract().unwrap(); self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val)); - Ok(Some(ctx.ctx.bool_type().const_int(val as u64, false).into())) + Ok(Some(ctx.ctx.i8_type().const_int(val as u64, false).into())) } else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 { let val: f64 = obj.extract().unwrap(); self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val)); diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 10ba4b0..e4dd6ba 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -4,6 +4,7 @@ use crate::{ codegen::{ concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, get_llvm_type, + get_llvm_abi_type, irrt::*, stmt::gen_raise, CodeGenContext, CodeGenTask, @@ -103,7 +104,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { SymbolValue::I64(v) => self.ctx.i64_type().const_int(*v as u64, true).into(), SymbolValue::U32(v) => self.ctx.i32_type().const_int(*v as u64, false).into(), SymbolValue::U64(v) => self.ctx.i64_type().const_int(*v as u64, false).into(), - SymbolValue::Bool(v) => self.ctx.bool_type().const_int(*v as u64, true).into(), + SymbolValue::Bool(v) => self.ctx.i8_type().const_int(*v as u64, true).into(), SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(), SymbolValue::Str(v) => { let str_ptr = @@ -160,6 +161,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { } } + /// See [get_llvm_type]. pub fn get_llvm_type( &mut self, generator: &mut dyn CodeGenerator, @@ -177,6 +179,24 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { ) } + /// See [get_llvm_abi_type]. + pub fn get_llvm_abi_type( + &mut self, + generator: &mut dyn CodeGenerator, + ty: Type, + ) -> BasicTypeEnum<'ctx> { + get_llvm_abi_type( + self.ctx, + &self.module, + generator, + &mut self.unifier, + self.top_level, + &mut self.type_cache, + &self.primitives, + ty, + ) + } + pub fn gen_const( &mut self, generator: &mut dyn CodeGenerator, @@ -186,7 +206,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { match value { Constant::Bool(v) => { assert!(self.unifier.unioned(ty, self.primitives.bool)); - let ty = self.ctx.bool_type(); + let ty = self.ctx.i8_type(); ty.const_int(if *v { 1 } else { 0 }, false).into() } Constant::Int(val) => { @@ -713,18 +733,19 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>( let ret_type = if ctx.unifier.unioned(fun.0.ret, ctx.primitives.none) { None } else { - Some(ctx.get_llvm_type(generator, fun.0.ret)) + Some(ctx.get_llvm_abi_type(generator, fun.0.ret)) }; let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type)); let mut byvals = Vec::new(); - let mut params = - args.iter().enumerate().map(|(i, arg)| match ctx.get_llvm_type(generator, arg.ty) { + let mut params = args.iter().enumerate() + .map(|(i, arg)| match ctx.get_llvm_abi_type(generator, arg.ty) { BasicTypeEnum::StructType(ty) if is_extern => { byvals.push((i, ty)); ty.ptr_type(AddressSpace::default()).into() }, x => x - }.into()).collect_vec(); + }.into()) + .collect_vec(); if has_sret { params.insert(0, ret_type.unwrap().ptr_type(AddressSpace::default()).into()); } @@ -746,6 +767,25 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>( } fun_val }); + + // Convert boolean parameter values into i1 + let param_vals = (&fun_val.get_params()).iter().zip(param_vals) + .map(|(p, v)| { + if p.is_int_value() && v.is_int_value() { + let expected_ty = p.into_int_value().get_type(); + let param_val = v.into_int_value(); + + if expected_ty.get_bit_width() == 1 && param_val.get_type().get_bit_width() != 1 { + generator.bool_to_i1(ctx, param_val) + } else { + param_val + }.into() + } else { + v + } + }) + .collect_vec(); + Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call")) } @@ -951,12 +991,14 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>( let val = ctx.build_gep_and_load(arr_ptr, &[tmp], Some("val")); generator.gen_assign(ctx, target, val.into())?; } + for cond in ifs.iter() { let result = generator .gen_expr(ctx, cond)? .unwrap() .to_basic_value_enum(ctx, generator, cond.custom.unwrap())? .into_int_value(); + let result = generator.bool_to_i1(ctx, result); let succ = ctx.ctx.append_basic_block(current, "then"); ctx.builder.build_conditional_branch(result, succ, test_bb); @@ -1223,11 +1265,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>( let a_bb = ctx.ctx.append_basic_block(current, "a"); let b_bb = ctx.ctx.append_basic_block(current, "b"); let cont_bb = ctx.ctx.append_basic_block(current, "cont"); - ctx.builder.build_conditional_branch(left, a_bb, b_bb); + ctx.builder.build_conditional_branch(generator.bool_to_i1(ctx, left), a_bb, b_bb); let (a, b) = match op { Boolop::Or => { ctx.builder.position_at_end(a_bb); - let a = ctx.ctx.bool_type().const_int(1, false); + let a = ctx.ctx.i8_type().const_int(1, false); ctx.builder.build_unconditional_branch(cont_bb); ctx.builder.position_at_end(b_bb); let b = generator @@ -1235,6 +1277,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>( .unwrap() .to_basic_value_enum(ctx, generator, values[1].custom.unwrap())? .into_int_value(); + let b = generator.bool_to_i8(ctx, b); ctx.builder.build_unconditional_branch(cont_bb); (a, b) } @@ -1245,15 +1288,16 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>( .unwrap() .to_basic_value_enum(ctx, generator, values[1].custom.unwrap())? .into_int_value(); + let a = generator.bool_to_i8(ctx, a); ctx.builder.build_unconditional_branch(cont_bb); ctx.builder.position_at_end(b_bb); - let b = ctx.ctx.bool_type().const_int(0, false); + let b = ctx.ctx.i8_type().const_zero(); ctx.builder.build_unconditional_branch(cont_bb); (a, b) } }; ctx.builder.position_at_end(cont_bb); - let phi = ctx.builder.build_phi(ctx.ctx.bool_type(), "phi"); + let phi = ctx.builder.build_phi(ctx.ctx.i8_type(), ""); phi.add_incoming(&[(&a, a_bb), (&b, b_bb)]); phi.as_basic_value().into() } diff --git a/nac3core/src/codegen/generator.rs b/nac3core/src/codegen/generator.rs index 33542b6..53a0996 100644 --- a/nac3core/src/codegen/generator.rs +++ b/nac3core/src/codegen/generator.rs @@ -1,5 +1,5 @@ use crate::{ - codegen::{expr::*, stmt::*, CodeGenContext}, + codegen::{expr::*, stmt::*, bool_to_i1, bool_to_i8, CodeGenContext}, symbol_resolver::ValueEnum, toplevel::{DefinitionId, TopLevelDef}, typecheck::typedef::{FunSignature, Type}, @@ -7,7 +7,7 @@ use crate::{ use inkwell::{ context::Context, types::{BasicTypeEnum, IntType}, - values::{BasicValueEnum, PointerValue}, + values::{BasicValueEnum, IntValue, PointerValue}, }; use nac3parser::ast::{Expr, Stmt, StrRef}; @@ -180,6 +180,24 @@ pub trait CodeGenerator { { gen_stmt(self, ctx, stmt) } + + /// Converts the value of [a boolean-like value][bool_value] into an `i1`. + fn bool_to_i1<'ctx, 'a>( + &self, + ctx: &CodeGenContext<'ctx, 'a>, + bool_value: IntValue<'ctx> + ) -> IntValue<'ctx> { + bool_to_i1(&ctx.builder, bool_value) + } + + /// Converts the value of [a boolean-like value][bool_value] into an `i8`. + fn bool_to_i8<'ctx, 'a>( + &self, + ctx: &CodeGenContext<'ctx, 'a>, + bool_value: IntValue<'ctx> + ) -> IntValue<'ctx> { + bool_to_i8(&ctx.builder, &ctx.ctx, bool_value) + } } pub struct DefaultCodeGenerator { diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 4684cad..300bd3c 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -9,6 +9,7 @@ use crate::{ use crossbeam::channel::{unbounded, Receiver, Sender}; use inkwell::{ AddressSpace, + IntPredicate, OptimizationLevel, attributes::{Attribute, AttributeLoc}, basic_block::BasicBlock, @@ -18,7 +19,7 @@ use inkwell::{ passes::PassBuilderOptions, targets::{CodeModel, RelocMode, Target, TargetMachine, TargetTriple}, types::{AnyType, BasicType, BasicTypeEnum}, - values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue}, + values::{BasicValueEnum, FunctionValue, IntValue, PhiValue, PointerValue}, debug_info::{ DebugInfoBuilder, DICompileUnit, DISubprogram, AsDIScope, DIFlagsConstants, DIScope }, @@ -354,6 +355,10 @@ pub struct CodeGenTask { pub id: usize, } +/// Retrieves the [LLVM type][BasicTypeEnum] corresponding to the [Type]. +/// +/// This function is used to obtain the in-memory representation of [ty], e.g. a `bool` variable +/// would be represented by an `i8`. fn get_llvm_type<'ctx>( ctx: &'ctx Context, module: &Module<'ctx>, @@ -465,6 +470,34 @@ fn get_llvm_type<'ctx>( }) } +/// Retrieves the [LLVM type][BasicTypeEnum] corresponding to the [Type]. +/// +/// This function is used mainly to obtain the ABI representation of [ty], e.g. a `bool` is +/// would be represented by an `i1`. +/// +/// The difference between the in-memory representation (as returned by [get_llvm_type]) and the +/// ABI representation is that the in-memory representation must be at least byte-sized and must +/// be byte-aligned for the variable to be addressable in memory, whereas there is no such +/// restriction for ABI representations. +fn get_llvm_abi_type<'ctx>( + ctx: &'ctx Context, + module: &Module<'ctx>, + generator: &mut dyn CodeGenerator, + unifier: &mut Unifier, + top_level: &TopLevelContext, + type_cache: &mut HashMap>, + primitives: &PrimitiveStore, + ty: Type, +) -> BasicTypeEnum<'ctx> { + // If the type is used in the definition of a function, return `i1` instead of `i8` for ABI + // consistency. + return if unifier.unioned(ty, primitives.bool) { + ctx.bool_type().into() + } else { + get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, primitives, ty) + } +} + fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool { fn need_sret_impl<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>, maybe_large: bool) -> bool { match ty { @@ -534,7 +567,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte (primitives.uint32, context.i32_type().into()), (primitives.uint64, context.i64_type().into()), (primitives.float, context.f64_type().into()), - (primitives.bool, context.bool_type().into()), + (primitives.bool, context.i8_type().into()), (primitives.str, { let name = "str"; match module.get_struct_type(name) { @@ -592,14 +625,14 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte let ret_type = if unifier.unioned(ret, primitives.none) { None } else { - Some(get_llvm_type(context, &module, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret)) + Some(get_llvm_abi_type(context, &module, 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)); let mut params = args .iter() .map(|arg| { - get_llvm_type( + get_llvm_abi_type( context, &module, generator, @@ -647,19 +680,35 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte let offset = if has_sret { 1 } else { 0 }; for (n, arg) in args.iter().enumerate() { let param = fn_val.get_nth_param((n as u32) + offset).unwrap(); - let alloca = builder.build_alloca( - get_llvm_type( - context, - &module, - generator, - &mut unifier, - top_level_ctx.as_ref(), - &mut type_cache, - &primitives, - arg.ty, - ), - &arg.name.to_string(), + let local_type = get_llvm_type( + context, + &module, + generator, + &mut unifier, + top_level_ctx.as_ref(), + &mut type_cache, + &primitives, + arg.ty, ); + let alloca = builder.build_alloca( + local_type, + &format!("{}.addr", &arg.name.to_string()), + ); + + // Remap boolean parameters into i8 + let param = if local_type.is_int_type() && param.is_int_value() { + let expected_ty = local_type.into_int_type(); + let param_val = param.into_int_value(); + + if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 { + bool_to_i8(&builder, &context, param_val) + } else { + param_val + }.into() + } else { + param + }; + builder.build_store(alloca, param); var_assignment.insert(arg.name, (alloca, None, 0)); } @@ -802,3 +851,40 @@ pub fn gen_func<'ctx, G: CodeGenerator>( Ok(()) }) } + +/// Converts the value of [a boolean-like value][bool_value] into an `i1`. +fn bool_to_i1<'ctx>(builder: &Builder<'ctx>, bool_value: IntValue<'ctx>) -> IntValue<'ctx> { + if bool_value.get_type().get_bit_width() != 1 { + builder.build_int_compare( + IntPredicate::NE, + bool_value, + bool_value.get_type().const_zero(), + "tobool" + ) + } else { + bool_value + } +} + +/// Converts the value of [a boolean-like value][bool_value] into an `i8`. +fn bool_to_i8<'ctx>( + builder: &Builder<'ctx>, + ctx: &'ctx Context, + bool_value: IntValue<'ctx> +) -> IntValue<'ctx> { + let value_bits = bool_value.get_type().get_bit_width(); + match value_bits { + 8 => bool_value, + 1 => builder.build_int_z_extend(bool_value, ctx.i8_type(), "frombool"), + _ => bool_to_i8( + builder, + ctx, + builder.build_int_compare( + IntPredicate::NE, + bool_value, + bool_value.get_type().const_zero(), + "" + ) + ), + } +} diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 7c2c833..6071b1c 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -411,7 +411,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>( test.custom.unwrap(), )?; if let BasicValueEnum::IntValue(test) = test { - ctx.builder.build_conditional_branch(test, body_bb, orelse_bb); + ctx.builder.build_conditional_branch(generator.bool_to_i1(ctx, test), body_bb, orelse_bb); } else { unreachable!() }; @@ -470,13 +470,11 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>( }; ctx.builder.build_unconditional_branch(test_bb); ctx.builder.position_at_end(test_bb); - let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum( - ctx, - generator, - test.custom.unwrap(), - )?; + let test = generator.gen_expr(ctx, test)? + .unwrap() + .to_basic_value_enum(ctx, generator, test.custom.unwrap())?; if let BasicValueEnum::IntValue(test) = test { - ctx.builder.build_conditional_branch(test, body_bb, orelse_bb); + ctx.builder.build_conditional_branch(generator.bool_to_i1(ctx, test), body_bb, orelse_bb); } else { unreachable!() }; @@ -1021,6 +1019,7 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>( ctx: &mut CodeGenContext<'ctx, 'a>, value: &Option>>>, ) -> Result<(), String> { + let func = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap(); let value = value .as_ref() .map(|v_expr| { @@ -1039,8 +1038,26 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>( ctx.builder.build_store(ctx.return_buffer.unwrap(), value.unwrap()); ctx.builder.build_return(None); } else { + // Remap boolean return type into i1 + let value = value.map(|v| { + let expected_ty = func.get_type().get_return_type().unwrap(); + let ret_val = v.as_basic_value_enum(); + + if expected_ty.is_int_type() && ret_val.is_int_value() { + let ret_type = expected_ty.into_int_type(); + let ret_val = ret_val.into_int_value(); + + if ret_type.get_bit_width() == 1 && ret_val.get_type().get_bit_width() != 1 { + generator.bool_to_i1(ctx, ret_val) + } else { + ret_val + }.into() + } else { + ret_val + } + }); let value = value.as_ref().map(|v| v as &dyn BasicValue); - ctx.builder.build_return(value); + ctx.builder.build_return(value.into()); } Ok(()) } diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index f4c87bf..5d07ac3 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -1050,7 +1050,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { let uint32 = ctx.primitives.uint32; let uint64 = ctx.primitives.uint64; let float = ctx.primitives.float; - let llvm_i1 = ctx.ctx.bool_type().as_basic_type_enum(); + let llvm_i8 = ctx.ctx.i8_type().as_basic_type_enum(); let llvm_i32 = ctx.ctx.i32_type().as_basic_type_enum(); let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum(); let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum(); @@ -1060,7 +1060,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?; let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b); let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) { - ("llvm.umin.i1", llvm_i1) + ("llvm.umin.i8", llvm_i8) } else if is_type(m_ty, n_ty) && is_type(n_ty, int32) { ("llvm.smin.i32", llvm_i32) } else if is_type(m_ty, n_ty) && is_type(n_ty, int64) { @@ -1112,7 +1112,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { let uint32 = ctx.primitives.uint32; let uint64 = ctx.primitives.uint64; let float = ctx.primitives.float; - let llvm_i1 = ctx.ctx.bool_type().as_basic_type_enum(); + let llvm_i8 = ctx.ctx.i8_type().as_basic_type_enum(); let llvm_i32 = ctx.ctx.i32_type().as_basic_type_enum(); let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum(); let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum(); @@ -1122,7 +1122,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?; let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b); let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) { - ("llvm.umax.i1", llvm_i1) + ("llvm.umax.i8", llvm_i8) } else if is_type(m_ty, n_ty) && is_type(n_ty, int32) { ("llvm.smax.i32", llvm_i32) } else if is_type(m_ty, n_ty) && is_type(n_ty, int64) { diff --git a/nac3standalone/demo/src/bool_literal_condition.py b/nac3standalone/demo/src/bool_literal_condition.py new file mode 100644 index 0000000..f998fed --- /dev/null +++ b/nac3standalone/demo/src/bool_literal_condition.py @@ -0,0 +1,30 @@ +# Different cases for using boolean variables in boolean contexts. +# Tests whether all boolean variables (expressed as i8s) are lowered into i1s before used in branching instruction (`br`) + +def bfunc(b: bool) -> bool: + return not b + +def run() -> int32: + b1 = True + b2 = False + + if b1: + pass + + if not b2: + pass + + while b2: + pass + + l = [i for i in range(10) if b2] + + b_and = True and False + b_or = True or False + + b_and = b1 and b2 + b_or = b1 or b2 + + bfunc(b1) + + return 0 \ No newline at end of file diff --git a/nac3standalone/demo/src/list_slice_issue315.py b/nac3standalone/demo/src/list_slice_issue315.py new file mode 100644 index 0000000..b91f99b --- /dev/null +++ b/nac3standalone/demo/src/list_slice_issue315.py @@ -0,0 +1,17 @@ +@extern +def output_int32(x: int32): + ... + +@extern +def output_int32_list(x: list[int32]): + ... + +def run() -> int32: + bl = [True, False] + + bl1 = bl[:] + bl1[1:] = [True] + output_int32_list([int32(b) for b in bl1]) + output_int32_list([int32(b) for b in bl1]) + + return 0