From 0c5f8979045ff05e713bb1b7341496012189650f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Feb 2019 08:49:27 -0500 Subject: [PATCH] fix `@bitCast` when src/dest types have mismatched handle_is_ptr * separate BitCast and BitCastGen instructions * closes #991 * closes #1934 * unrelated: fix typo in docs (thanks gamester for pointing it out) --- doc/langref.html.in | 2 +- src/all_types.hpp | 8 +++++++ src/codegen.cpp | 38 +++++++++++++++++++++++++------- src/ir.cpp | 28 +++++++++++++++++++---- src/ir_print.cpp | 13 ++++++++--- test/stage1/behavior/bitcast.zig | 18 +++++++++++++++ 6 files changed, 91 insertions(+), 16 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3a3eaf708c..9a286cb063 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7823,7 +7823,7 @@ Environments: coreclr opencl

- The Zig Standard Library ({#syntax#}@import("std"){#endsyntax#}) has architecture, environment, and operating sytsem + The Zig Standard Library ({#syntax#}@import("std"){#endsyntax#}) has architecture, environment, and operating system abstractions, and thus takes additional work to support more platforms. Not all standard library code requires operating system abstractions, however, so things such as generic data structures work an all above platforms. diff --git a/src/all_types.hpp b/src/all_types.hpp index fe36eb3a5d..29598672cd 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2198,6 +2198,7 @@ enum IrInstructionId { IrInstructionIdPtrCastSrc, IrInstructionIdPtrCastGen, IrInstructionIdBitCast, + IrInstructionIdBitCastGen, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, @@ -3055,6 +3056,13 @@ struct IrInstructionBitCast { IrInstruction *value; }; +struct IrInstructionBitCastGen { + IrInstruction base; + + IrInstruction *operand; + LLVMValueRef tmp_ptr; +}; + struct IrInstructionWidenOrShorten { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 6c9e8ccc4b..c43611e8af 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3073,14 +3073,32 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, - IrInstructionBitCast *instruction) + IrInstructionBitCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; - LLVMValueRef value = ir_llvm_value(g, instruction->value); - // We either bitcast the value directly or bitcast the pointer which does a pointer cast - LLVMTypeRef wanted_type_ref = handle_is_ptr(wanted_type) ? - LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; - return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); + ZigType *actual_type = instruction->operand->value.type; + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + + bool wanted_is_ptr = handle_is_ptr(wanted_type); + bool actual_is_ptr = handle_is_ptr(actual_type); + if (wanted_is_ptr == actual_is_ptr) { + // We either bitcast the value directly or bitcast the pointer which does a pointer cast + LLVMTypeRef wanted_type_ref = wanted_is_ptr ? + LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; + return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); + } else if (actual_is_ptr) { + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(wanted_type->type_ref, 0); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); + uint32_t alignment = get_abi_alignment(g, actual_type); + return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); + } else { + assert(instruction->tmp_ptr != nullptr); + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(actual_type->type_ref, 0); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, wanted_ptr_type_ref, ""); + uint32_t alignment = get_abi_alignment(g, wanted_type); + gen_store_untyped(g, value, bitcasted_ptr, alignment, false); + return instruction->tmp_ptr; + } } static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, @@ -5469,6 +5487,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrCastSrc: case IrInstructionIdCmpxchgSrc: case IrInstructionIdLoadPtr: + case IrInstructionIdBitCast: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5565,8 +5584,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); case IrInstructionIdPtrCastGen: return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); - case IrInstructionIdBitCast: - return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); + case IrInstructionIdBitCastGen: + return ir_render_bit_cast(g, executable, (IrInstructionBitCastGen *)instruction); case IrInstructionIdWidenOrShorten: return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); case IrInstructionIdPtrToInt: @@ -6764,6 +6783,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdLoadPtrGen) { IrInstructionLoadPtrGen *load_ptr_inst = (IrInstructionLoadPtrGen *)instruction; slot = &load_ptr_inst->tmp_ptr; + } else if (instruction->id == IrInstructionIdBitCastGen) { + IrInstructionBitCastGen *bit_cast_inst = (IrInstructionBitCastGen *)instruction; + slot = &bit_cast_inst->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 5fae81f0ce..36f11cb108 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -744,6 +744,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { return IrInstructionIdBitCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCastGen *) { + return IrInstructionIdBitCastGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { return IrInstructionIdWidenOrShorten; } @@ -2317,12 +2321,25 @@ static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *s instruction->dest_type = dest_type; instruction->value = value; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_bit_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *operand, ZigType *ty) +{ + IrInstructionBitCastGen *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_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { @@ -21335,9 +21352,10 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ return result; } - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, value); - result->value.type = dest_type; + IrInstruction *result = ir_build_bit_cast_gen(ira, source_instr, value, dest_type); + if (handle_is_ptr(dest_type) && !handle_is_ptr(src_type)) { + ir_add_alloca(ira, result, dest_type); + } return result; } @@ -22347,6 +22365,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdAssertZero: case IrInstructionIdResizeSlice: case IrInstructionIdLoadPtrGen: + case IrInstructionIdBitCastGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22804,6 +22823,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrCastSrc: case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: + case IrInstructionIdBitCastGen: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c9058d4bc7..4572b30ded 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -920,14 +920,18 @@ static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruc static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); - if (instruction->dest_type) { - ir_print_other_instruction(irp, instruction->dest_type); - } + ir_print_other_instruction(irp, instruction->dest_type); fprintf(irp->f, ","); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); } +static void ir_print_bit_cast_gen(IrPrint *irp, IrInstructionBitCastGen *instruction) { + fprintf(irp->f, "@bitCast("); + ir_print_other_instruction(irp, instruction->operand); + fprintf(irp->f, ")"); +} + static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); @@ -1692,6 +1696,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); break; + case IrInstructionIdBitCastGen: + ir_print_bit_cast_gen(irp, (IrInstructionBitCastGen *)instruction); + break; case IrInstructionIdWidenOrShorten: ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); break; diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index d409975548..e86c50885e 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -94,3 +94,21 @@ test "@bitCast extern structs at runtime and comptime" { S.doTheTest(); comptime S.doTheTest(); } + +test "bitcast packed struct to integer and back" { + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + }; + const S = struct { + fn doTheTest() void { + var move = LevelUpMove{ .move_id = 1, .level = 2 }; + var v = @bitCast(u16, move); + var back_to_a_move = @bitCast(LevelUpMove, v); + expect(back_to_a_move.move_id == 1); + expect(back_to_a_move.level == 2); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +}