parent
db31c2524d
commit
2bb795dc45
|
@ -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<AstNode *> 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;
|
||||
|
||||
|
|
138
src/codegen.cpp
138
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);
|
||||
|
|
59
src/ir.cpp
59
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<IrInstructionResizeSlice>(&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:
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue