mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 19:23:08 +00:00
more alignment improvements
* add alignment capability for fn protos * add @alignCast * fix some ast rendering code * fix some ir rendering code * add error for pointer cast increasing alignment * update allocators in std to correctly align See #37
This commit is contained in:
parent
910a96f046
commit
898d65baa9
@ -1252,6 +1252,7 @@ enum BuiltinFnId {
|
|||||||
BuiltinFnIdShlExact,
|
BuiltinFnIdShlExact,
|
||||||
BuiltinFnIdShrExact,
|
BuiltinFnIdShrExact,
|
||||||
BuiltinFnIdSetEvalBranchQuota,
|
BuiltinFnIdSetEvalBranchQuota,
|
||||||
|
BuiltinFnIdAlignCast,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFnEntry {
|
struct BuiltinFnEntry {
|
||||||
@ -1274,6 +1275,7 @@ enum PanicMsgId {
|
|||||||
PanicMsgIdSliceWidenRemainder,
|
PanicMsgIdSliceWidenRemainder,
|
||||||
PanicMsgIdUnwrapMaybeFail,
|
PanicMsgIdUnwrapMaybeFail,
|
||||||
PanicMsgIdInvalidErrorCode,
|
PanicMsgIdInvalidErrorCode,
|
||||||
|
PanicMsgIdIncorrectAlignment,
|
||||||
|
|
||||||
PanicMsgIdCount,
|
PanicMsgIdCount,
|
||||||
};
|
};
|
||||||
@ -1856,6 +1858,7 @@ enum IrInstructionId {
|
|||||||
IrInstructionIdTypeId,
|
IrInstructionIdTypeId,
|
||||||
IrInstructionIdSetEvalBranchQuota,
|
IrInstructionIdSetEvalBranchQuota,
|
||||||
IrInstructionIdPtrTypeOf,
|
IrInstructionIdPtrTypeOf,
|
||||||
|
IrInstructionIdAlignCast,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IrInstruction {
|
struct IrInstruction {
|
||||||
@ -2462,6 +2465,7 @@ struct IrInstructionFnProto {
|
|||||||
IrInstruction base;
|
IrInstruction base;
|
||||||
|
|
||||||
IrInstruction **param_types;
|
IrInstruction **param_types;
|
||||||
|
IrInstruction *align_value;
|
||||||
IrInstruction *return_type;
|
IrInstruction *return_type;
|
||||||
bool is_var_args;
|
bool is_var_args;
|
||||||
};
|
};
|
||||||
@ -2638,6 +2642,13 @@ struct IrInstructionPtrTypeOf {
|
|||||||
bool is_volatile;
|
bool is_volatile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionAlignCast {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
IrInstruction *align_bytes;
|
||||||
|
IrInstruction *target;
|
||||||
|
};
|
||||||
|
|
||||||
static const size_t slice_ptr_index = 0;
|
static const size_t slice_ptr_index = 0;
|
||||||
static const size_t slice_len_index = 1;
|
static const size_t slice_len_index = 1;
|
||||||
|
|
||||||
|
|||||||
@ -884,6 +884,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
|||||||
buf_appendf(&fn_type->name, "%s...", comma);
|
buf_appendf(&fn_type->name, "%s...", comma);
|
||||||
}
|
}
|
||||||
buf_appendf(&fn_type->name, ")");
|
buf_appendf(&fn_type->name, ")");
|
||||||
|
if (fn_type_id->alignment != 0) {
|
||||||
|
buf_appendf(&fn_type->name, " align %" PRIu32, fn_type_id->alignment);
|
||||||
|
}
|
||||||
if (fn_type_id->return_type->id != TypeTableEntryIdVoid) {
|
if (fn_type_id->return_type->id != TypeTableEntryIdVoid) {
|
||||||
buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
|
buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
|
||||||
}
|
}
|
||||||
@ -1058,12 +1061,13 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
|
|||||||
fn_type_id->is_var_args = fn_proto->is_var_args;
|
fn_type_id->is_var_args = fn_proto->is_var_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
|
static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, uint32_t alignment) {
|
||||||
assert(proto_node->type == NodeTypeFnProto);
|
assert(proto_node->type == NodeTypeFnProto);
|
||||||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||||
|
|
||||||
FnTypeId fn_type_id = {0};
|
FnTypeId fn_type_id = {0};
|
||||||
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
|
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
|
||||||
|
fn_type_id.alignment = alignment;
|
||||||
|
|
||||||
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
|
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
|
||||||
AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
|
AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
|
||||||
@ -2056,23 +2060,23 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
|
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
|
||||||
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope);
|
|
||||||
|
|
||||||
if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) {
|
|
||||||
tld_fn->base.resolution = TldResolutionInvalid;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
uint32_t alignment = 0;
|
||||||
if (fn_proto->align_expr != nullptr) {
|
if (fn_proto->align_expr != nullptr) {
|
||||||
if (!analyze_const_align(g, tld_fn->base.parent_scope, fn_proto->align_expr,
|
if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &alignment)) {
|
||||||
&fn_table_entry->align_bytes))
|
|
||||||
{
|
|
||||||
fn_table_entry->type_entry = g->builtin_types.entry_invalid;
|
fn_table_entry->type_entry = g->builtin_types.entry_invalid;
|
||||||
tld_fn->base.resolution = TldResolutionInvalid;
|
tld_fn->base.resolution = TldResolutionInvalid;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, alignment);
|
||||||
|
|
||||||
|
if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) {
|
||||||
|
tld_fn->base.resolution = TldResolutionInvalid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fn_table_entry->type_entry->data.fn.is_generic) {
|
if (!fn_table_entry->type_entry->data.fn.is_generic) {
|
||||||
g->fn_protos.append(fn_table_entry);
|
g->fn_protos.append(fn_table_entry);
|
||||||
|
|
||||||
@ -2663,6 +2667,9 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
|
|||||||
if (expected_type->id == TypeTableEntryIdFn &&
|
if (expected_type->id == TypeTableEntryIdFn &&
|
||||||
actual_type->id == TypeTableEntryIdFn)
|
actual_type->id == TypeTableEntryIdFn)
|
||||||
{
|
{
|
||||||
|
if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
|
if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3384,6 +3391,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) {
|
|||||||
result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
|
result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
|
||||||
result += id->is_var_args ? (uint32_t)1931444534 : 0;
|
result += id->is_var_args ? (uint32_t)1931444534 : 0;
|
||||||
result += hash_ptr(id->return_type);
|
result += hash_ptr(id->return_type);
|
||||||
|
result += id->alignment * 0xd3b3f3e2;
|
||||||
for (size_t i = 0; i < id->param_count; i += 1) {
|
for (size_t i = 0; i < id->param_count; i += 1) {
|
||||||
FnTypeParamInfo *info = &id->param_info[i];
|
FnTypeParamInfo *info = &id->param_info[i];
|
||||||
result += info->is_noalias ? (uint32_t)892356923 : 0;
|
result += info->is_noalias ? (uint32_t)892356923 : 0;
|
||||||
@ -3396,7 +3404,8 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
|
|||||||
if (a->cc != b->cc ||
|
if (a->cc != b->cc ||
|
||||||
a->return_type != b->return_type ||
|
a->return_type != b->return_type ||
|
||||||
a->is_var_args != b->is_var_args ||
|
a->is_var_args != b->is_var_args ||
|
||||||
a->param_count != b->param_count)
|
a->param_count != b->param_count ||
|
||||||
|
a->alignment != b->alignment)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -953,7 +953,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||||||
render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
|
render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
|
||||||
fprintf(ar->f, "[");
|
fprintf(ar->f, "[");
|
||||||
render_node_grouped(ar, node->data.slice_expr.start);
|
render_node_grouped(ar, node->data.slice_expr.start);
|
||||||
fprintf(ar->f, "...");
|
fprintf(ar->f, "..");
|
||||||
if (node->data.slice_expr.end)
|
if (node->data.slice_expr.end)
|
||||||
render_node_grouped(ar, node->data.slice_expr.end);
|
render_node_grouped(ar, node->data.slice_expr.end);
|
||||||
fprintf(ar->f, "]");
|
fprintf(ar->f, "]");
|
||||||
|
|||||||
@ -688,6 +688,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
|
|||||||
return buf_create_from_str("reached unreachable code");
|
return buf_create_from_str("reached unreachable code");
|
||||||
case PanicMsgIdInvalidErrorCode:
|
case PanicMsgIdInvalidErrorCode:
|
||||||
return buf_create_from_str("invalid error code");
|
return buf_create_from_str("invalid error code");
|
||||||
|
case PanicMsgIdIncorrectAlignment:
|
||||||
|
return buf_create_from_str("incorrect alignment");
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -2605,6 +2607,72 @@ static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef get_default_aligned_load(CodeGen *g, LLVMValueRef ptr) {
|
||||||
|
LLVMValueRef result = LLVMBuildLoad(g->builder, ptr, "");
|
||||||
|
LLVMSetAlignment(result, LLVMABIAlignmentOfType(g->target_data_ref, LLVMGetElementType(LLVMTypeOf(ptr))));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, IrInstructionAlignCast *instruction) {
|
||||||
|
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
|
||||||
|
assert(target_val);
|
||||||
|
|
||||||
|
bool want_debug_safety = ir_want_debug_safety(g, &instruction->base);
|
||||||
|
if (!want_debug_safety) {
|
||||||
|
return target_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeTableEntry *target_type = instruction->base.value.type;
|
||||||
|
uint32_t align_bytes;
|
||||||
|
LLVMValueRef ptr_val;
|
||||||
|
|
||||||
|
if (target_type->id == TypeTableEntryIdPointer) {
|
||||||
|
align_bytes = target_type->data.pointer.alignment;
|
||||||
|
ptr_val = target_val;
|
||||||
|
} else if (target_type->id == TypeTableEntryIdFn) {
|
||||||
|
align_bytes = target_type->data.fn.fn_type_id.alignment;
|
||||||
|
ptr_val = target_val;
|
||||||
|
} else if (target_type->id == TypeTableEntryIdMaybe &&
|
||||||
|
target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)
|
||||||
|
{
|
||||||
|
align_bytes = target_type->data.maybe.child_type->data.pointer.alignment;
|
||||||
|
ptr_val = target_val;
|
||||||
|
} else if (target_type->id == TypeTableEntryIdMaybe &&
|
||||||
|
target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
|
||||||
|
{
|
||||||
|
align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment;
|
||||||
|
ptr_val = target_val;
|
||||||
|
} else if (target_type->id == TypeTableEntryIdStruct && target_type->data.structure.is_slice) {
|
||||||
|
TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||||
|
align_bytes = slice_ptr_type->data.pointer.alignment;
|
||||||
|
|
||||||
|
size_t ptr_index = target_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||||
|
LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, "");
|
||||||
|
ptr_val = get_default_aligned_load(g, ptr_val_ptr);
|
||||||
|
} else {
|
||||||
|
zig_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(align_bytes != 1);
|
||||||
|
|
||||||
|
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||||
|
LLVMValueRef ptr_as_int_val = LLVMBuildPtrToInt(g->builder, ptr_val, usize->type_ref, "");
|
||||||
|
LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->type_ref, align_bytes - 1, false);
|
||||||
|
LLVMValueRef anded_val = LLVMBuildAnd(g->builder, ptr_as_int_val, alignment_minus_1, "");
|
||||||
|
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, LLVMConstNull(usize->type_ref), "");
|
||||||
|
|
||||||
|
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastOk");
|
||||||
|
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastFail");
|
||||||
|
|
||||||
|
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||||
|
gen_debug_safety_crash(g, PanicMsgIdIncorrectAlignment);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||||
|
|
||||||
|
return target_val;
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
|
static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
|
||||||
switch (atomic_order) {
|
switch (atomic_order) {
|
||||||
@ -3350,6 +3418,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||||||
return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction);
|
return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction);
|
||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
|
return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
|
||||||
|
case IrInstructionIdAlignCast:
|
||||||
|
return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -4633,6 +4703,7 @@ static void define_builtin_fns(CodeGen *g) {
|
|||||||
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
|
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
|
||||||
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
|
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
|
||||||
create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1);
|
create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1);
|
||||||
|
create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *bool_to_str(bool b) {
|
static const char *bool_to_str(bool b) {
|
||||||
|
|||||||
194
src/ir.cpp
194
src/ir.cpp
@ -555,6 +555,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) {
|
|||||||
return IrInstructionIdPtrTypeOf;
|
return IrInstructionIdPtrTypeOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) {
|
||||||
|
return IrInstructionIdAlignCast;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||||
T *special_instruction = allocate<T>(1);
|
T *special_instruction = allocate<T>(1);
|
||||||
@ -1899,10 +1903,11 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
|
|||||||
}
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
IrInstruction **param_types, IrInstruction *return_type, bool is_var_args)
|
IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args)
|
||||||
{
|
{
|
||||||
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
|
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
|
||||||
instruction->param_types = param_types;
|
instruction->param_types = param_types;
|
||||||
|
instruction->align_value = align_value;
|
||||||
instruction->return_type = return_type;
|
instruction->return_type = return_type;
|
||||||
instruction->is_var_args = is_var_args;
|
instruction->is_var_args = is_var_args;
|
||||||
|
|
||||||
@ -1912,6 +1917,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
|
|||||||
for (size_t i = 0; i < param_count; i += 1) {
|
for (size_t i = 0; i < param_count; i += 1) {
|
||||||
ir_ref_instruction(param_types[i], irb->current_basic_block);
|
ir_ref_instruction(param_types[i], irb->current_basic_block);
|
||||||
}
|
}
|
||||||
|
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
|
||||||
ir_ref_instruction(return_type, irb->current_basic_block);
|
ir_ref_instruction(return_type, irb->current_basic_block);
|
||||||
|
|
||||||
return &instruction->base;
|
return &instruction->base;
|
||||||
@ -2219,6 +2225,19 @@ static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scop
|
|||||||
return &instruction->base;
|
return &instruction->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_align_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
IrInstruction *align_bytes, IrInstruction *target)
|
||||||
|
{
|
||||||
|
IrInstructionAlignCast *instruction = ir_build_instruction<IrInstructionAlignCast>(irb, scope, source_node);
|
||||||
|
instruction->align_bytes = align_bytes;
|
||||||
|
instruction->target = target;
|
||||||
|
|
||||||
|
ir_ref_instruction(align_bytes, irb->current_basic_block);
|
||||||
|
ir_ref_instruction(target, irb->current_basic_block);
|
||||||
|
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -2738,6 +2757,10 @@ static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instr
|
|||||||
if (param_index < instruction->base.source_node->data.fn_proto.params.length) {
|
if (param_index < instruction->base.source_node->data.fn_proto.params.length) {
|
||||||
return instruction->param_types[param_index];
|
return instruction->param_types[param_index];
|
||||||
}
|
}
|
||||||
|
size_t next_index = param_index - instruction->base.source_node->data.fn_proto.params.length;
|
||||||
|
if (next_index == 0 && instruction->align_value != nullptr) {
|
||||||
|
return instruction->align_value;
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2925,6 +2948,14 @@ static IrInstruction *ir_instruction_ptrtypeof_get_dep(IrInstructionPtrTypeOf *i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_instruction_aligncast_get_dep(IrInstructionAlignCast *instruction, size_t index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0: return instruction->align_bytes;
|
||||||
|
case 1: return instruction->target;
|
||||||
|
default: return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
case IrInstructionIdInvalid:
|
case IrInstructionIdInvalid:
|
||||||
@ -3121,6 +3152,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
|||||||
return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index);
|
return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index);
|
||||||
case IrInstructionIdPtrTypeOf:
|
case IrInstructionIdPtrTypeOf:
|
||||||
return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index);
|
return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index);
|
||||||
|
case IrInstructionIdAlignCast:
|
||||||
|
return ir_instruction_aligncast_get_dep((IrInstructionAlignCast *) instruction, index);
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -4531,6 +4564,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||||||
|
|
||||||
return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
|
return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
|
||||||
}
|
}
|
||||||
|
case BuiltinFnIdAlignCast:
|
||||||
|
{
|
||||||
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||||
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||||
|
if (arg0_value == irb->codegen->invalid_instruction)
|
||||||
|
return arg0_value;
|
||||||
|
|
||||||
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||||
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||||
|
if (arg1_value == irb->codegen->invalid_instruction)
|
||||||
|
return arg1_value;
|
||||||
|
|
||||||
|
return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -6060,11 +6107,18 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||||||
param_types[i] = type_value;
|
param_types[i] = type_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IrInstruction *align_value = nullptr;
|
||||||
|
if (node->data.fn_proto.align_expr != nullptr) {
|
||||||
|
align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope);
|
||||||
|
if (align_value == irb->codegen->invalid_instruction)
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
|
IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
|
||||||
if (return_type == irb->codegen->invalid_instruction)
|
if (return_type == irb->codegen->invalid_instruction)
|
||||||
return irb->codegen->invalid_instruction;
|
return irb->codegen->invalid_instruction;
|
||||||
|
|
||||||
return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type, is_var_args);
|
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
|
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
|
||||||
@ -8316,16 +8370,30 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
|||||||
}
|
}
|
||||||
|
|
||||||
// explicit cast from []T to []u8 or []u8 to []T
|
// explicit cast from []T to []u8 or []u8 to []T
|
||||||
if (is_slice(wanted_type) && is_slice(actual_type) &&
|
if (is_slice(wanted_type) && is_slice(actual_type)) {
|
||||||
(is_u8(wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type) ||
|
TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||||
is_u8(actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type)) &&
|
TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||||
(wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
|
if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) &&
|
||||||
!actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const))
|
(wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const))
|
||||||
{
|
{
|
||||||
|
uint32_t src_align_bytes = get_ptr_align(actual_ptr_type);
|
||||||
|
uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type);
|
||||||
|
|
||||||
|
if (dest_align_bytes > src_align_bytes) {
|
||||||
|
ErrorMsg *msg = ir_add_error(ira, source_instr,
|
||||||
|
buf_sprintf("cast increases pointer alignment"));
|
||||||
|
add_error_note(ira->codegen, msg, source_instr->source_node,
|
||||||
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), src_align_bytes));
|
||||||
|
add_error_note(ira->codegen, msg, source_instr->source_node,
|
||||||
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), dest_align_bytes));
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ir_emit_global_runtime_side_effect(ira, source_instr))
|
if (!ir_emit_global_runtime_side_effect(ira, source_instr))
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
|
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// explicit cast from [N]u8 to []const T
|
// explicit cast from [N]u8 to []const T
|
||||||
if (is_slice(wanted_type) &&
|
if (is_slice(wanted_type) &&
|
||||||
@ -10226,7 +10294,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
|
|||||||
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
|
||||||
nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec);
|
nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec);
|
||||||
|
|
||||||
ir_resolve_align(ira, align_result, &impl_fn->align_bytes);
|
uint32_t align_bytes = 0;
|
||||||
|
ir_resolve_align(ira, align_result, &align_bytes);
|
||||||
|
impl_fn->align_bytes = align_bytes;
|
||||||
|
inst_fn_type_id.alignment = align_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -13728,11 +13799,9 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
|||||||
TypeTableEntry *return_type;
|
TypeTableEntry *return_type;
|
||||||
|
|
||||||
if (array_type->id == TypeTableEntryIdArray) {
|
if (array_type->id == TypeTableEntryIdArray) {
|
||||||
uint32_t normal_array_alignment = get_abi_alignment(ira->codegen, array_type);
|
|
||||||
uint32_t align_bytes = (ptr_type->data.pointer.alignment >= normal_array_alignment) ?
|
|
||||||
normal_array_alignment : 1;
|
|
||||||
TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
|
TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
|
||||||
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, align_bytes, 0, 0);
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
||||||
|
ptr_type->data.pointer.alignment, 0, 0);
|
||||||
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
||||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||||
TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type,
|
TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type,
|
||||||
@ -14237,6 +14306,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instruction->align_value != nullptr) {
|
||||||
|
if (!ir_resolve_align(ira, instruction->align_value->other, &fn_type_id.alignment))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
IrInstruction *return_type_value = instruction->return_type->other;
|
IrInstruction *return_type_value = instruction->return_type->other;
|
||||||
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
|
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
|
||||||
if (type_is_invalid(fn_type_id.return_type))
|
if (type_is_invalid(fn_type_id.return_type))
|
||||||
@ -14866,6 +14940,90 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInst
|
|||||||
return ira->codegen->builtin_types.entry_type;
|
return ira->codegen->builtin_types.entry_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) {
|
||||||
|
uint32_t align_bytes;
|
||||||
|
IrInstruction *align_bytes_inst = instruction->align_bytes->other;
|
||||||
|
if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
IrInstruction *target = instruction->target->other;
|
||||||
|
TypeTableEntry *target_type = target->value.type;
|
||||||
|
if (type_is_invalid(target_type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
TypeTableEntry *result_type;
|
||||||
|
uint32_t old_align_bytes;
|
||||||
|
|
||||||
|
if (target_type->id == TypeTableEntryIdPointer) {
|
||||||
|
result_type = get_pointer_to_type_extra(ira->codegen,
|
||||||
|
target_type->data.pointer.child_type,
|
||||||
|
target_type->data.pointer.is_const, target_type->data.pointer.is_volatile,
|
||||||
|
align_bytes,
|
||||||
|
target_type->data.pointer.bit_offset, target_type->data.pointer.unaligned_bit_count);
|
||||||
|
} else if (target_type->id == TypeTableEntryIdFn) {
|
||||||
|
FnTypeId fn_type_id = target_type->data.fn.fn_type_id;
|
||||||
|
old_align_bytes = fn_type_id.alignment;
|
||||||
|
fn_type_id.alignment = align_bytes;
|
||||||
|
result_type = get_fn_type(ira->codegen, &fn_type_id);
|
||||||
|
} else if (target_type->id == TypeTableEntryIdMaybe &&
|
||||||
|
target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)
|
||||||
|
{
|
||||||
|
TypeTableEntry *ptr_type = target_type->data.maybe.child_type;
|
||||||
|
old_align_bytes = ptr_type->data.pointer.alignment;
|
||||||
|
TypeTableEntry *better_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
||||||
|
ptr_type->data.pointer.child_type,
|
||||||
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
||||||
|
align_bytes,
|
||||||
|
ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count);
|
||||||
|
|
||||||
|
result_type = get_maybe_type(ira->codegen, better_ptr_type);
|
||||||
|
} else if (target_type->id == TypeTableEntryIdMaybe &&
|
||||||
|
target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
|
||||||
|
{
|
||||||
|
FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id;
|
||||||
|
old_align_bytes = fn_type_id.alignment;
|
||||||
|
fn_type_id.alignment = align_bytes;
|
||||||
|
TypeTableEntry *fn_type = get_fn_type(ira->codegen, &fn_type_id);
|
||||||
|
result_type = get_maybe_type(ira->codegen, fn_type);
|
||||||
|
} else if (is_slice(target_type)) {
|
||||||
|
TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||||
|
old_align_bytes = slice_ptr_type->data.pointer.alignment;
|
||||||
|
TypeTableEntry *result_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
||||||
|
slice_ptr_type->data.pointer.child_type,
|
||||||
|
slice_ptr_type->data.pointer.is_const, slice_ptr_type->data.pointer.is_volatile,
|
||||||
|
align_bytes,
|
||||||
|
slice_ptr_type->data.pointer.bit_offset, slice_ptr_type->data.pointer.unaligned_bit_count);
|
||||||
|
result_type = get_slice_type(ira->codegen, result_ptr_type);
|
||||||
|
} else {
|
||||||
|
ir_add_error(ira, target,
|
||||||
|
buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name)));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr_is_comptime(target)) {
|
||||||
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
||||||
|
if (!val)
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||||
|
copy_const_val(out_val, val, false);
|
||||||
|
out_val->type = result_type;
|
||||||
|
return result_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrInstruction *result;
|
||||||
|
if (align_bytes > old_align_bytes && align_bytes != 1) {
|
||||||
|
result = ir_build_align_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
||||||
|
align_bytes_inst, target);
|
||||||
|
} else {
|
||||||
|
result = ir_build_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
||||||
|
result_type, target, CastOpNoop);
|
||||||
|
}
|
||||||
|
ir_link_new_instruction(result, &instruction->base);
|
||||||
|
result->value.type = result_type;
|
||||||
|
return result_type;
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
case IrInstructionIdInvalid:
|
case IrInstructionIdInvalid:
|
||||||
@ -14877,6 +15035,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||||||
case IrInstructionIdStructFieldPtr:
|
case IrInstructionIdStructFieldPtr:
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
case IrInstructionIdInitEnum:
|
case IrInstructionIdInitEnum:
|
||||||
|
case IrInstructionIdMaybeWrap:
|
||||||
|
case IrInstructionIdErrWrapCode:
|
||||||
|
case IrInstructionIdErrWrapPayload:
|
||||||
|
case IrInstructionIdCast:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
case IrInstructionIdReturn:
|
case IrInstructionIdReturn:
|
||||||
return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
|
return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
|
||||||
@ -15046,11 +15208,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||||||
return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction);
|
return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction);
|
||||||
case IrInstructionIdPtrTypeOf:
|
case IrInstructionIdPtrTypeOf:
|
||||||
return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction);
|
return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction);
|
||||||
case IrInstructionIdMaybeWrap:
|
case IrInstructionIdAlignCast:
|
||||||
case IrInstructionIdErrWrapCode:
|
return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
|
||||||
case IrInstructionIdErrWrapPayload:
|
|
||||||
case IrInstructionIdCast:
|
|
||||||
zig_panic("TODO analyze more instructions");
|
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -15228,6 +15387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
case IrInstructionIdOffsetOf:
|
case IrInstructionIdOffsetOf:
|
||||||
case IrInstructionIdTypeId:
|
case IrInstructionIdTypeId:
|
||||||
|
case IrInstructionIdAlignCast:
|
||||||
return false;
|
return false;
|
||||||
case IrInstructionIdAsm:
|
case IrInstructionIdAsm:
|
||||||
{
|
{
|
||||||
|
|||||||
@ -174,10 +174,16 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr
|
|||||||
if (decl_var_instruction->var_type) {
|
if (decl_var_instruction->var_type) {
|
||||||
fprintf(irp->f, "%s %s: ", var_or_const, name);
|
fprintf(irp->f, "%s %s: ", var_or_const, name);
|
||||||
ir_print_other_instruction(irp, decl_var_instruction->var_type);
|
ir_print_other_instruction(irp, decl_var_instruction->var_type);
|
||||||
fprintf(irp->f, " = ");
|
fprintf(irp->f, " ");
|
||||||
} else {
|
} else {
|
||||||
fprintf(irp->f, "%s %s = ", var_or_const, name);
|
fprintf(irp->f, "%s %s ", var_or_const, name);
|
||||||
}
|
}
|
||||||
|
if (decl_var_instruction->align_value) {
|
||||||
|
fprintf(irp->f, "align ");
|
||||||
|
ir_print_other_instruction(irp, decl_var_instruction->align_value);
|
||||||
|
fprintf(irp->f, " ");
|
||||||
|
}
|
||||||
|
fprintf(irp->f, "= ");
|
||||||
ir_print_other_instruction(irp, decl_var_instruction->init_value);
|
ir_print_other_instruction(irp, decl_var_instruction->init_value);
|
||||||
if (decl_var_instruction->var->is_comptime != nullptr) {
|
if (decl_var_instruction->var->is_comptime != nullptr) {
|
||||||
fprintf(irp->f, " // comptime = ");
|
fprintf(irp->f, " // comptime = ");
|
||||||
@ -640,7 +646,7 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) {
|
|||||||
ir_print_other_instruction(irp, instruction->ptr);
|
ir_print_other_instruction(irp, instruction->ptr);
|
||||||
fprintf(irp->f, "[");
|
fprintf(irp->f, "[");
|
||||||
ir_print_other_instruction(irp, instruction->start);
|
ir_print_other_instruction(irp, instruction->start);
|
||||||
fprintf(irp->f, "...");
|
fprintf(irp->f, "..");
|
||||||
if (instruction->end)
|
if (instruction->end)
|
||||||
ir_print_other_instruction(irp, instruction->end);
|
ir_print_other_instruction(irp, instruction->end);
|
||||||
fprintf(irp->f, "]");
|
fprintf(irp->f, "]");
|
||||||
@ -745,7 +751,13 @@ static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) {
|
|||||||
ir_print_other_instruction(irp, instruction->param_types[i]);
|
ir_print_other_instruction(irp, instruction->param_types[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fprintf(irp->f, ")->");
|
fprintf(irp->f, ")");
|
||||||
|
if (instruction->align_value != nullptr) {
|
||||||
|
fprintf(irp->f, " align ");
|
||||||
|
ir_print_other_instruction(irp, instruction->align_value);
|
||||||
|
fprintf(irp->f, " ");
|
||||||
|
}
|
||||||
|
fprintf(irp->f, "->");
|
||||||
ir_print_other_instruction(irp, instruction->return_type);
|
ir_print_other_instruction(irp, instruction->return_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,6 +932,14 @@ static void ir_print_set_eval_branch_quota(IrPrint *irp, IrInstructionSetEvalBra
|
|||||||
fprintf(irp->f, ")");
|
fprintf(irp->f, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_align_cast(IrPrint *irp, IrInstructionAlignCast *instruction) {
|
||||||
|
fprintf(irp->f, "@alignCast(");
|
||||||
|
ir_print_other_instruction(irp, instruction->align_bytes);
|
||||||
|
fprintf(irp->f, ",");
|
||||||
|
ir_print_other_instruction(irp, instruction->target);
|
||||||
|
fprintf(irp->f, ")");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
ir_print_prefix(irp, instruction);
|
ir_print_prefix(irp, instruction);
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
@ -1213,6 +1233,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||||||
case IrInstructionIdSetEvalBranchQuota:
|
case IrInstructionIdSetEvalBranchQuota:
|
||||||
ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction);
|
ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdAlignCast:
|
||||||
|
ir_print_align_cast(irp, (IrInstructionAlignCast *)instruction);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fprintf(irp->f, "\n");
|
fprintf(irp->f, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -957,16 +957,21 @@ pub var global_allocator = mem.Allocator {
|
|||||||
var some_mem: [100 * 1024]u8 = undefined;
|
var some_mem: [100 * 1024]u8 = undefined;
|
||||||
var some_mem_index: usize = 0;
|
var some_mem_index: usize = 0;
|
||||||
|
|
||||||
fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 {
|
fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||||
const result = some_mem[some_mem_index .. some_mem_index + n];
|
const addr = @ptrToInt(&some_mem[some_mem_index]);
|
||||||
some_mem_index += n;
|
const rem = @rem(addr, alignment);
|
||||||
|
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||||
|
const adjusted_index = some_mem_index + march_forward_bytes;
|
||||||
|
const end_index = adjusted_index + n;
|
||||||
|
const result = some_mem[adjusted_index .. end_index];
|
||||||
|
some_mem_index = end_index;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
|
fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||||
const result = %return globalAlloc(self, new_size);
|
const result = %return globalAlloc(self, new_size, alignment);
|
||||||
@memcpy(result.ptr, old_mem.ptr, old_mem.len);
|
@memcpy(result.ptr, old_mem.ptr, old_mem.len);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn globalFree(self: &mem.Allocator, old_mem: []u8) { }
|
fn globalFree(self: &mem.Allocator, ptr: &u8) { }
|
||||||
|
|||||||
60
std/mem.zig
60
std/mem.zig
@ -11,21 +11,18 @@ pub const Cmp = math.Cmp;
|
|||||||
error NoMem;
|
error NoMem;
|
||||||
|
|
||||||
pub const Allocator = struct {
|
pub const Allocator = struct {
|
||||||
allocFn: fn (self: &Allocator, n: usize) -> %[]u8,
|
/// Allocate byte_count bytes and return them in a slice, with the
|
||||||
/// Note that old_mem may be a slice of length 0, in which case reallocFn
|
/// slicer's pointer aligned at least to alignment bytes.
|
||||||
/// should simply call allocFn.
|
allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
|
||||||
reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8,
|
|
||||||
/// Note that mem may be a slice of length 0, in which case freeFn
|
|
||||||
/// should do nothing.
|
|
||||||
freeFn: fn (self: &Allocator, mem: []u8),
|
|
||||||
|
|
||||||
/// Aborts the program if an allocation fails.
|
/// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr
|
||||||
fn checkedAlloc(self: &Allocator, comptime T: type, n: usize) -> []T {
|
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
|
||||||
alloc(self, T, n) %% |err| debug.panic("allocation failure: {}", @errorName(err))
|
|
||||||
}
|
freeFn: fn (self: &Allocator, ptr: &u8),
|
||||||
|
|
||||||
fn create(self: &Allocator, comptime T: type) -> %&T {
|
fn create(self: &Allocator, comptime T: type) -> %&T {
|
||||||
&(%return self.alloc(T, 1))[0]
|
const slice = %return self.alloc(T, 1);
|
||||||
|
&slice[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy(self: &Allocator, ptr: var) {
|
fn destroy(self: &Allocator, ptr: var) {
|
||||||
@ -34,16 +31,29 @@ pub const Allocator = struct {
|
|||||||
|
|
||||||
fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
|
fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
|
||||||
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
||||||
([]T)(%return self.allocFn(self, byte_count))
|
const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T));
|
||||||
|
([]T)(@alignCast(@alignOf(T), byte_slice))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
|
fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
|
||||||
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
if (old_mem.len == 0) {
|
||||||
([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count))
|
return self.alloc(T, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free(self: &Allocator, mem: var) {
|
// Assert that old_mem.ptr is properly aligned.
|
||||||
self.freeFn(self, ([]u8)(mem));
|
_ = @alignCast(@alignOf(T), old_mem.ptr);
|
||||||
|
|
||||||
|
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
||||||
|
const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, @alignOf(T));
|
||||||
|
([]T)(@alignCast(@alignOf(T), byte_slice))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free(self: &Allocator, memory: var) {
|
||||||
|
const const_slice = ([]const u8)(memory);
|
||||||
|
if (memory.len == 0)
|
||||||
|
return;
|
||||||
|
const ptr = @intToPtr(&u8, @ptrToInt(const_slice.ptr));
|
||||||
|
self.freeFn(self, ptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,24 +89,28 @@ pub const IncrementingAllocator = struct {
|
|||||||
_ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
|
_ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc(allocator: &Allocator, n: usize) -> %[]u8 {
|
fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||||
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
|
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
|
||||||
const new_end_index = self.end_index + n;
|
const addr = @ptrToInt(&self.bytes[self.end_index]);
|
||||||
|
const rem = @rem(addr, alignment);
|
||||||
|
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||||
|
const adjusted_index = self.end_index + march_forward_bytes;
|
||||||
|
const new_end_index = adjusted_index + n;
|
||||||
if (new_end_index > self.bytes.len) {
|
if (new_end_index > self.bytes.len) {
|
||||||
return error.NoMem;
|
return error.NoMem;
|
||||||
}
|
}
|
||||||
const result = self.bytes[self.end_index..new_end_index];
|
const result = self.bytes[adjusted_index .. new_end_index];
|
||||||
self.end_index = new_end_index;
|
self.end_index = new_end_index;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
|
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||||
const result = %return alloc(allocator, new_size);
|
const result = %return alloc(allocator, new_size, alignment);
|
||||||
copy(u8, result, old_mem);
|
copy(u8, result, old_mem);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free(allocator: &Allocator, bytes: []u8) {
|
fn free(allocator: &Allocator, bytes: &u8) {
|
||||||
// Do nothing. That's the point of an incrementing allocator.
|
// Do nothing. That's the point of an incrementing allocator.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -62,3 +62,24 @@ fn testBytesAlign(b: u8) {
|
|||||||
const ptr = @ptrCast(&u32, &bytes[0]);
|
const ptr = @ptrCast(&u32, &bytes[0]);
|
||||||
assert(*ptr == 0x33333333);
|
assert(*ptr == 0x33333333);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "specifying alignment allows slice cast" {
|
||||||
|
testBytesAlignSlice(0x33);
|
||||||
|
}
|
||||||
|
fn testBytesAlignSlice(b: u8) {
|
||||||
|
var bytes align 4 = []u8{b, b, b, b};
|
||||||
|
const slice = ([]u32)(bytes[0..]);
|
||||||
|
assert(slice[0] == 0x33333333);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "@alignCast" {
|
||||||
|
var x: u32 align 4 = 1;
|
||||||
|
expectsOnly1(&x);
|
||||||
|
assert(x == 2);
|
||||||
|
}
|
||||||
|
fn expectsOnly1(x: &align 1 u32) {
|
||||||
|
expects4(@alignCast(4, x));
|
||||||
|
}
|
||||||
|
fn expects4(x: &align 4 u32) {
|
||||||
|
*x += 1;
|
||||||
|
}
|
||||||
|
|||||||
@ -277,7 +277,7 @@ fn cast128Float(x: u128) -> f128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "const slice widen cast" {
|
test "const slice widen cast" {
|
||||||
const bytes = []u8{0x12, 0x12, 0x12, 0x12};
|
const bytes align 4 = []u8{0x12, 0x12, 0x12, 0x12};
|
||||||
|
|
||||||
const u32_value = ([]const u32)(bytes[0..])[0];
|
const u32_value = ([]const u32)(bytes[0..])[0];
|
||||||
assert(u32_value == 0x12121212);
|
assert(u32_value == 0x12121212);
|
||||||
|
|||||||
@ -404,7 +404,7 @@ test "cast slice to u8 slice" {
|
|||||||
bytes[6] = 0;
|
bytes[6] = 0;
|
||||||
bytes[7] = 0;
|
bytes[7] = 0;
|
||||||
assert(big_thing_slice[1] == 0);
|
assert(big_thing_slice[1] == 0);
|
||||||
const big_thing_again = ([]i32)(bytes);
|
const big_thing_again = ([]align 1 i32)(bytes);
|
||||||
assert(big_thing_again[2] == 3);
|
assert(big_thing_again[2] == 3);
|
||||||
big_thing_again[2] = -1;
|
big_thing_again[2] = -1;
|
||||||
assert(bytes[8] == @maxValue(u8));
|
assert(bytes[8] == @maxValue(u8));
|
||||||
|
|||||||
@ -2022,4 +2022,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
|
|||||||
".tmp_source.zig:3:17: error: cast increases pointer alignment",
|
".tmp_source.zig:3:17: error: cast increases pointer alignment",
|
||||||
".tmp_source.zig:3:38: note: '&u8' has alignment 1",
|
".tmp_source.zig:3:38: note: '&u8' has alignment 1",
|
||||||
".tmp_source.zig:3:27: note: '&u32' has alignment 4");
|
".tmp_source.zig:3:27: note: '&u32' has alignment 4");
|
||||||
|
|
||||||
|
cases.add("increase pointer alignment in slice resize",
|
||||||
|
\\export fn entry() -> u32 {
|
||||||
|
\\ var bytes = []u8{0x01, 0x02, 0x03, 0x04};
|
||||||
|
\\ return ([]u32)(bytes[0..])[0];
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
".tmp_source.zig:3:19: error: cast increases pointer alignment",
|
||||||
|
".tmp_source.zig:3:19: note: '[]u8' has alignment 1",
|
||||||
|
".tmp_source.zig:3:19: note: '[]u32' has alignment 4");
|
||||||
|
|
||||||
|
cases.add("@alignCast expects pointer or slice",
|
||||||
|
\\export fn entry() {
|
||||||
|
\\ @alignCast(4, u32(3))
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,8 +200,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||||||
\\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
|
\\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
|
||||||
\\ if (x.len == 0) return error.Whatever;
|
\\ if (x.len == 0) return error.Whatever;
|
||||||
\\}
|
\\}
|
||||||
\\fn widenSlice(slice: []const u8) -> []const i32 {
|
\\fn widenSlice(slice: []align 1 const u8) -> []align 1 const i32 {
|
||||||
\\ ([]const i32)(slice)
|
\\ ([]align 1 const i32)(slice)
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -261,4 +261,22 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||||||
\\ return error(x);
|
\\ return error(x);
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cases.addDebugSafety("@alignCast misaligned",
|
||||||
|
\\pub fn panic(message: []const u8) -> noreturn {
|
||||||
|
\\ @breakpoint();
|
||||||
|
\\ while (true) {}
|
||||||
|
\\}
|
||||||
|
\\error Wrong;
|
||||||
|
\\pub fn main() -> %void {
|
||||||
|
\\ var array align 4 = []u32{0x11111111, 0x11111111};
|
||||||
|
\\ const bytes = ([]u8)(array[0..]);
|
||||||
|
\\ if (foo(bytes) != 0x11111111) return error.Wrong;
|
||||||
|
\\}
|
||||||
|
\\fn foo(bytes: []u8) -> u32 {
|
||||||
|
\\ const slice4 = bytes[1..5];
|
||||||
|
\\ const int_slice = ([]u32)(@alignCast(4, slice4));
|
||||||
|
\\ return int_slice[0];
|
||||||
|
\\}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user