From 2bb795dc455823e76ef3e0c9b3fcee6bcb15fddb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Feb 2019 10:07:11 -0500 Subject: [PATCH] `@sliceToBytes` works at comptime closes #262 --- src/all_types.hpp | 33 +++--- src/codegen.cpp | 138 ++++++++++++++------------ src/ir.cpp | 59 +++++++++-- src/ir_print.cpp | 9 ++ test/stage1/behavior.zig | 1 + test/stage1/behavior/slicetobytes.zig | 29 ++++++ 6 files changed, 185 insertions(+), 84 deletions(-) create mode 100644 test/stage1/behavior/slicetobytes.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 2a359fa49..0ee0fef2c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -630,19 +630,6 @@ struct AstNodeUnwrapOptional { AstNode *expr; }; -enum CastOp { - CastOpNoCast, // signifies the function call expression is not a cast - CastOpNoop, // fn call expr is a cast, but does nothing - CastOpIntToFloat, - CastOpFloatToInt, - CastOpBoolToInt, - CastOpResizeSlice, - CastOpNumLitToConcrete, - CastOpErrSet, - CastOpBitCast, - CastOpPtrOfArrayToSlice, -}; - struct AstNodeFnCallExpr { AstNode *fn_ref_expr; ZigList params; @@ -2142,6 +2129,7 @@ enum IrInstructionId { IrInstructionIdConst, IrInstructionIdReturn, IrInstructionIdCast, + IrInstructionIdResizeSlice, IrInstructionIdContainerInitList, IrInstructionIdContainerInitFields, IrInstructionIdStructInit, @@ -2503,6 +2491,18 @@ struct IrInstructionReturn { IrInstruction *value; }; +enum CastOp { + CastOpNoCast, // signifies the function call expression is not a cast + CastOpNoop, // fn call expr is a cast, but does nothing + CastOpIntToFloat, + CastOpFloatToInt, + CastOpBoolToInt, + CastOpNumLitToConcrete, + CastOpErrSet, + CastOpBitCast, + CastOpPtrOfArrayToSlice, +}; + // TODO get rid of this instruction, replace with instructions for each op code struct IrInstructionCast { IrInstruction base; @@ -2513,6 +2513,13 @@ struct IrInstructionCast { LLVMValueRef tmp_ptr; }; +struct IrInstructionResizeSlice { + IrInstruction base; + + IrInstruction *operand; + LLVMValueRef tmp_ptr; +}; + struct IrInstructionContainerInitList { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index dbe029129..bc4005c87 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2882,6 +2882,76 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in } } +static LLVMValueRef ir_render_resize_slice(CodeGen *g, IrExecutable *executable, + IrInstructionResizeSlice *instruction) +{ + ZigType *actual_type = instruction->operand->value.type; + ZigType *wanted_type = instruction->base.value.type; + LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand); + assert(expr_val); + + assert(instruction->tmp_ptr); + assert(wanted_type->id == ZigTypeIdStruct); + assert(wanted_type->data.structure.is_slice); + assert(actual_type->id == ZigTypeIdStruct); + assert(actual_type->data.structure.is_slice); + + ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; + ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; + ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; + + + size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; + size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; + size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; + size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; + + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, + wanted_type->data.structure.fields[0].type_entry->type_ref, ""); + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + (unsigned)wanted_ptr_index, ""); + gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); + + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); + LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); + uint64_t src_size = type_size(g, actual_child_type); + uint64_t dest_size = type_size(g, wanted_child_type); + + LLVMValueRef new_len; + if (dest_size == 1) { + LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); + new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); + } else if (src_size == 1) { + LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); + } else { + zig_unreachable(); + } + + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + (unsigned)wanted_len_index, ""); + gen_store_untyped(g, new_len, dest_len_ptr, 0, false); + + + return instruction->tmp_ptr; +} + static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { @@ -2896,69 +2966,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); case CastOpNoop: return expr_val; - case CastOpResizeSlice: - { - assert(cast_instruction->tmp_ptr); - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdStruct); - assert(actual_type->data.structure.is_slice); - - ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; - ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_ptr_index, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); - LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); - uint64_t src_size = type_size(g, actual_child_type); - uint64_t dest_size = type_size(g, wanted_child_type); - - LLVMValueRef new_len; - if (dest_size == 1) { - LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); - new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); - } else if (src_size == 1) { - LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); - if (ir_want_runtime_safety(g, &cast_instruction->base)) { - LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); - } else { - zig_unreachable(); - } - - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_len_index, ""); - gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - - - return cast_instruction->tmp_ptr; - } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); if (actual_type->data.integral.is_signed) { @@ -5625,6 +5632,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); case IrInstructionIdAssertZero: return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); + case IrInstructionIdResizeSlice: + return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction); } zig_unreachable(); } @@ -6716,6 +6725,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdCmpxchgGen) { IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdResizeSlice) { + IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; + slot = &resize_slice_instruction->tmp_ptr; } else if (instruction->id == IrInstructionIdVectorToArray) { IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); diff --git a/src/ir.cpp b/src/ir.cpp index 03555c78f..a385592e2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -456,6 +456,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResizeSlice *) { + return IrInstructionIdResizeSlice; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } @@ -1475,6 +1479,19 @@ static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *sourc return &decl_var_instruction->base; } +static IrInstruction *ir_build_resize_slice(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *operand, ZigType *ty) +{ + IrInstructionResizeSlice *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ty; + instruction->operand = operand; + + ir_ref_instruction(operand, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -9674,9 +9691,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } const_val->type = new_type; break; - case CastOpResizeSlice: - // can't do it - zig_unreachable(); case CastOpIntToFloat: { assert(new_type->id == ZigTypeIdFloat); @@ -9740,9 +9754,7 @@ static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, Z static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { - if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && - cast_op != CastOpResizeSlice) - { + if (instr_is_comptime(value) || !type_has_bits(wanted_type)) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) @@ -19082,7 +19094,9 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru } } - return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type); + ir_add_alloca(ira, result, dest_slice_type); + return result; } static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { @@ -19109,7 +19123,34 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct alignment, 0, 0); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + if (instr_is_comptime(target)) { + ConstExprValue *target_val = ir_resolve_const(ira, target, UndefBad); + if (target_val == nullptr) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_const(ira, &instruction->base, dest_slice_type); + result->value.data.x_struct.fields = create_const_vals(2); + + ConstExprValue *ptr_val = &result->value.data.x_struct.fields[slice_ptr_index]; + ConstExprValue *target_ptr_val = &target_val->data.x_struct.fields[slice_ptr_index]; + copy_const_val(ptr_val, target_ptr_val, false); + ptr_val->type = dest_ptr_type; + + ConstExprValue *len_val = &result->value.data.x_struct.fields[slice_len_index]; + len_val->special = ConstValSpecialStatic; + len_val->type = ira->codegen->builtin_types.entry_usize; + ConstExprValue *target_len_val = &target_val->data.x_struct.fields[slice_len_index]; + ZigType *elem_type = src_ptr_type->data.pointer.child_type; + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, type_size(ira->codegen, elem_type)); + bigint_mul(&len_val->data.x_bigint, &target_len_val->data.x_bigint, &elem_size_bigint); + + return result; + } + + IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type); + ir_add_alloca(ira, result, dest_slice_type); + return result; } static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { @@ -22274,6 +22315,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdArrayToVector: case IrInstructionIdVectorToArray: case IrInstructionIdAssertZero: + case IrInstructionIdResizeSlice: zig_unreachable(); case IrInstructionIdReturn: @@ -22673,6 +22715,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCmpxchgGen: case IrInstructionIdCmpxchgSrc: case IrInstructionIdAssertZero: + case IrInstructionIdResizeSlice: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 75da24d1a..935519fde 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -990,6 +990,12 @@ static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruct fprintf(irp->f, ")"); } +static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) { + fprintf(irp->f, "@resizeSlice("); + ir_print_other_instruction(irp, instruction->operand); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1852,6 +1858,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAssertZero: ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); break; + case IrInstructionIdResizeSlice: + ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 708bb91ac..525d65d7f 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -61,6 +61,7 @@ comptime { _ = @import("behavior/reflection.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); _ = @import("behavior/slice.zig"); + _ = @import("behavior/slicetobytes.zig"); _ = @import("behavior/struct.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); diff --git a/test/stage1/behavior/slicetobytes.zig b/test/stage1/behavior/slicetobytes.zig new file mode 100644 index 000000000..b86b38bea --- /dev/null +++ b/test/stage1/behavior/slicetobytes.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; + +test "@sliceToBytes packed struct at runtime and comptime" { + const Foo = packed struct { + a: u4, + b: u4, + }; + const S = struct { + fn doTheTest() void { + var foo: Foo = undefined; + var slice = @sliceToBytes(((*[1]Foo)(&foo))[0..1]); + slice[0] = 0x13; + switch (builtin.endian) { + builtin.Endian.Big => { + expect(foo.a == 0x1); + expect(foo.b == 0x3); + }, + builtin.Endian.Little => { + expect(foo.a == 0x3); + expect(foo.b == 0x1); + }, + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +}