Commit 0c5f897904

Andrew Kelley <andrew@ziglang.org>
2019-02-22 14:49:27
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)
1 parent cbce61a
Changed files (6)
doc/langref.html.in
@@ -7823,7 +7823,7 @@ Environments:
   coreclr
   opencl</code></pre>
       <p>
-      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.
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;
 
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);
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<IrInstructionBitCastGen>(
+            &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:
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;
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();
+}