| author | |
| committer | |
| log | e6428f94013132b6a6284b053afb36d35af63c59 |
| tree | 86275c7d4d6c0729a0905e4e9e2abca1371f1fed |
| parent | 58d5c37409a7fc7fea2d6c536bf53c0193c5266a |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Consider a (legal according to the `@bitCast` rules) conversion from u16
to [2]u8: since the former is a scalar and the latter is a pointer
(arrays are represented at pointers in the codegen phase) we have to
allocate a temporary slot on the stack and then bitcast the resulting
pointer to the desired destination type.
Beware that this means the lifetime of the resulting value is the same
of the function it's contained in and for all intents and purposes
should be regarded as a local (eg. it should not escape).
Closes #4395
Closes #5121
3 files changed, 24 insertions(+), 5 deletions(-)
src/codegen.cpp+5-1| ... | @@ -3331,12 +3331,16 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutableGen *executable, | ... | @@ -3331,12 +3331,16 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutableGen *executable, |
| 3331 | LLVMPointerType(get_llvm_type(g, wanted_type), 0) : get_llvm_type(g, wanted_type); | 3331 | LLVMPointerType(get_llvm_type(g, wanted_type), 0) : get_llvm_type(g, wanted_type); |
| 3332 | return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); | 3332 | return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); |
| 3333 | } else if (actual_is_ptr) { | 3333 | } else if (actual_is_ptr) { |
| 3334 | // A scalar is wanted but we got a pointer | ||
| 3334 | LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); | 3335 | LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); |
| 3335 | LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); | 3336 | LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); |
| 3336 | uint32_t alignment = get_abi_alignment(g, actual_type); | 3337 | uint32_t alignment = get_abi_alignment(g, actual_type); |
| 3337 | return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); | 3338 | return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); |
| 3338 | } else { | 3339 | } else { |
| 3339 | zig_unreachable(); | 3340 | // A pointer is wanted but we got a scalar |
| 3341 | assert(actual_type->id == ZigTypeIdPointer); | ||
| 3342 | LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); | ||
| 3343 | return LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); | ||
| 3340 | } | 3344 | } |
| 3341 | } | 3345 | } |
| 3342 | 3346 |
src/ir.cpp+12-4| ... | @@ -28878,8 +28878,11 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn | ... | @@ -28878,8 +28878,11 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn |
| 28878 | if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) | 28878 | if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) |
| 28879 | return ira->codegen->invalid_inst_gen; | 28879 | return ira->codegen->invalid_inst_gen; |
| 28880 | 28880 | ||
| 28881 | uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); | 28881 | const bool src_is_ptr = handle_is_ptr(ira->codegen, src_type); |
| 28882 | uint64_t src_size_bytes = type_size(ira->codegen, src_type); | 28882 | const bool dest_is_ptr = handle_is_ptr(ira->codegen, dest_type); |
| 28883 | |||
| 28884 | const uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); | ||
| 28885 | const uint64_t src_size_bytes = type_size(ira->codegen, src_type); | ||
| 28883 | if (dest_size_bytes != src_size_bytes) { | 28886 | if (dest_size_bytes != src_size_bytes) { |
| 28884 | ir_add_error(ira, source_instr, | 28887 | ir_add_error(ira, source_instr, |
| 28885 | buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, | 28888 | buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, |
| ... | @@ -28888,8 +28891,8 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn | ... | @@ -28888,8 +28891,8 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn |
| 28888 | return ira->codegen->invalid_inst_gen; | 28891 | return ira->codegen->invalid_inst_gen; |
| 28889 | } | 28892 | } |
| 28890 | 28893 | ||
| 28891 | uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); | 28894 | const uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); |
| 28892 | uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); | 28895 | const uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); |
| 28893 | if (dest_size_bits != src_size_bits) { | 28896 | if (dest_size_bits != src_size_bits) { |
| 28894 | ir_add_error(ira, source_instr, | 28897 | ir_add_error(ira, source_instr, |
| 28895 | buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", | 28898 | buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", |
| ... | @@ -28911,6 +28914,11 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn | ... | @@ -28911,6 +28914,11 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn |
| 28911 | return result; | 28914 | return result; |
| 28912 | } | 28915 | } |
| 28913 | 28916 | ||
| 28917 | if (dest_is_ptr && !src_is_ptr) { | ||
| 28918 | // Spill the scalar into a local memory location and take its address | ||
| 28919 | value = ir_get_ref(ira, source_instr, value, false, false); | ||
| 28920 | } | ||
| 28921 | |||
| 28914 | return ir_build_bit_cast_gen(ira, source_instr, value, dest_type); | 28922 | return ir_build_bit_cast_gen(ira, source_instr, value, dest_type); |
| 28915 | } | 28923 | } |
| 28916 | 28924 |
test/stage1/behavior/bitcast.zig+7| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; | ||
| 4 | const maxInt = std.math.maxInt; | 5 | const maxInt = std.math.maxInt; |
| 5 | 6 | ||
| 6 | test "@bitCast i32 -> u32" { | 7 | test "@bitCast i32 -> u32" { |
| ... | @@ -187,3 +188,9 @@ test "triple level result location with bitcast sandwich passed as tuple element | ... | @@ -187,3 +188,9 @@ test "triple level result location with bitcast sandwich passed as tuple element |
| 187 | }; | 188 | }; |
| 188 | S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))}); | 189 | S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))}); |
| 189 | } | 190 | } |
| 191 | |||
| 192 | test "bitcast generates a temporary value" { | ||
| 193 | var y = @as(u16, 0x55AA); | ||
| 194 | const x = @bitCast(u16, @bitCast([2]u8, y)); | ||
| 195 | expectEqual(y, x); | ||
| 196 | } |