mirror of
https://github.com/ziglang/zig.git
synced 2024-11-30 17:12:31 +00:00
IR: implement memcpy, memset, and slice expression
This commit is contained in:
parent
fb21570630
commit
ef63bc9cca
@ -417,7 +417,7 @@ Function Operation
|
||||
@shlWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a << b
|
||||
```
|
||||
|
||||
### @memset(dest: &T, c: u8, byte_count: usize)
|
||||
### @memset(dest: &u8, c: u8, byte_count: usize)
|
||||
|
||||
This function sets a region of memory to `c`. `dest` is a pointer.
|
||||
|
||||
@ -428,7 +428,9 @@ level code will not use this function, instead using something like this:
|
||||
for (destSlice) |*b| *b = c;
|
||||
```
|
||||
|
||||
### @memcpy(noalias dest: &T, noalias source: &const T, byte_count: usize)
|
||||
The optimizer is intelligent enough to turn the above snippet into a memset.
|
||||
|
||||
### @memcpy(noalias dest: &u8, noalias source: &const u8, byte_count: usize)
|
||||
|
||||
This function copies bytes from one region of memory to another. `dest` and
|
||||
`source` are both pointers and must not overlap.
|
||||
@ -441,6 +443,8 @@ const mem = @import("std").mem;
|
||||
mem.copy(destSlice, sourceSlice);
|
||||
```
|
||||
|
||||
The optimizer is intelligent enough to turn the above snippet into a memcpy.
|
||||
|
||||
### @breakpoint()
|
||||
|
||||
This function inserts a platform-specific debug trap instruction which causes
|
||||
|
@ -1415,6 +1415,9 @@ enum IrInstructionId {
|
||||
IrInstructionIdIntType,
|
||||
IrInstructionIdBoolNot,
|
||||
IrInstructionIdAlloca,
|
||||
IrInstructionIdMemset,
|
||||
IrInstructionIdMemcpy,
|
||||
IrInstructionIdSlice,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -1924,6 +1927,32 @@ struct IrInstructionAlloca {
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionMemset {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *dest_ptr;
|
||||
IrInstruction *byte;
|
||||
IrInstruction *count;
|
||||
};
|
||||
|
||||
struct IrInstructionMemcpy {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *dest_ptr;
|
||||
IrInstruction *src_ptr;
|
||||
IrInstruction *count;
|
||||
};
|
||||
|
||||
struct IrInstructionSlice {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *ptr;
|
||||
IrInstruction *start;
|
||||
IrInstruction *end;
|
||||
bool is_const;
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
enum LValPurpose {
|
||||
LValPurposeNone,
|
||||
LValPurposeAssign,
|
||||
|
@ -849,11 +849,23 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
fprintf(ar->f, "%scontinue", inline_str);
|
||||
break;
|
||||
}
|
||||
case NodeTypeSliceExpr:
|
||||
{
|
||||
render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
|
||||
fprintf(ar->f, "[");
|
||||
render_node_grouped(ar, node->data.slice_expr.start);
|
||||
fprintf(ar->f, "...");
|
||||
if (node->data.slice_expr.end)
|
||||
render_node_grouped(ar, node->data.slice_expr.end);
|
||||
fprintf(ar->f, "]");
|
||||
if (node->data.slice_expr.is_const)
|
||||
fprintf(ar->f, "const");
|
||||
break;
|
||||
}
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeErrorValueDecl:
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeSliceExpr:
|
||||
case NodeTypeStructField:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeZeroesLiteral:
|
||||
|
156
src/codegen.cpp
156
src/codegen.cpp
@ -1906,6 +1906,153 @@ static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrIns
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
|
||||
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
|
||||
LLVMValueRef char_val = ir_llvm_value(g, instruction->byte);
|
||||
LLVMValueRef len_val = ir_llvm_value(g, instruction->count);
|
||||
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
|
||||
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
|
||||
|
||||
LLVMValueRef params[] = {
|
||||
dest_ptr_casted, // dest pointer
|
||||
char_val, // source pointer
|
||||
len_val, // byte count
|
||||
LLVMConstInt(LLVMInt32Type(), 1, false), // align in bytes
|
||||
LLVMConstNull(LLVMInt1Type()), // is volatile
|
||||
};
|
||||
|
||||
LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrInstructionMemcpy *instruction) {
|
||||
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
|
||||
LLVMValueRef src_ptr = ir_llvm_value(g, instruction->src_ptr);
|
||||
LLVMValueRef len_val = ir_llvm_value(g, instruction->count);
|
||||
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
|
||||
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
|
||||
LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
|
||||
|
||||
LLVMValueRef params[] = {
|
||||
dest_ptr_casted, // dest pointer
|
||||
src_ptr_casted, // source pointer
|
||||
len_val, // byte count
|
||||
LLVMConstInt(LLVMInt32Type(), 1, false), // align in bytes
|
||||
LLVMConstNull(LLVMInt1Type()), // is volatile
|
||||
};
|
||||
|
||||
LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) {
|
||||
TypeTableEntry *array_type = get_underlying_type(instruction->ptr->type_entry);
|
||||
|
||||
LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr;
|
||||
LLVMValueRef array_ptr = ir_llvm_value(g, instruction->ptr);
|
||||
|
||||
bool want_debug_safety = ir_want_debug_safety(g, &instruction->base);
|
||||
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
|
||||
LLVMValueRef end_val;
|
||||
if (instruction->end) {
|
||||
end_val = ir_llvm_value(g, instruction->end);
|
||||
} else {
|
||||
end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
|
||||
}
|
||||
|
||||
if (want_debug_safety) {
|
||||
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
if (instruction->end) {
|
||||
LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||
array_type->data.array.len, false);
|
||||
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
||||
start_val,
|
||||
};
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
|
||||
LLVMValueRef end_val = ir_llvm_value(g, instruction->end);
|
||||
|
||||
if (want_debug_safety) {
|
||||
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
}
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
|
||||
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
} else if (array_type->id == TypeTableEntryIdStruct) {
|
||||
assert(array_type->data.structure.is_slice);
|
||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
||||
|
||||
size_t ptr_index = array_type->data.structure.fields[0].gen_index;
|
||||
assert(ptr_index != SIZE_MAX);
|
||||
size_t len_index = array_type->data.structure.fields[1].gen_index;
|
||||
assert(len_index != SIZE_MAX);
|
||||
|
||||
LLVMValueRef prev_end = nullptr;
|
||||
if (!instruction->end || want_debug_safety) {
|
||||
LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
|
||||
prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
|
||||
}
|
||||
|
||||
LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
|
||||
LLVMValueRef end_val;
|
||||
if (instruction->end) {
|
||||
end_val = ir_llvm_value(g, instruction->end);
|
||||
} else {
|
||||
end_val = prev_end;
|
||||
}
|
||||
|
||||
if (want_debug_safety) {
|
||||
assert(prev_end);
|
||||
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
if (instruction->end) {
|
||||
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
|
||||
LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, len_index, "");
|
||||
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
|
||||
AstNode *source_node = instruction->source_node;
|
||||
Scope *scope = instruction->scope;
|
||||
@ -2008,6 +2155,12 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdMemset:
|
||||
return ir_render_memset(g, executable, (IrInstructionMemset *)instruction);
|
||||
case IrInstructionIdMemcpy:
|
||||
return ir_render_memcpy(g, executable, (IrInstructionMemcpy *)instruction);
|
||||
case IrInstructionIdSlice:
|
||||
return ir_render_slice(g, executable, (IrInstructionSlice *)instruction);
|
||||
case IrInstructionIdSwitchVar:
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdStructInit:
|
||||
@ -2590,6 +2743,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else if (instruction->id == IrInstructionIdAlloca) {
|
||||
IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction;
|
||||
slot = &alloca_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdSlice) {
|
||||
IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
|
||||
slot = &slice_instruction->tmp_ptr;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
757
src/ir.cpp
757
src/ir.cpp
@ -375,6 +375,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
|
||||
return IrInstructionIdAlloca;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
|
||||
return IrInstructionIdMemset;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemcpy *) {
|
||||
return IrInstructionIdMemcpy;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSlice *) {
|
||||
return IrInstructionIdSlice;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
|
||||
T *special_instruction = allocate<T>(1);
|
||||
@ -1514,6 +1526,76 @@ static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_in
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
|
||||
{
|
||||
IrInstructionMemset *instruction = ir_build_instruction<IrInstructionMemset>(irb, scope, source_node);
|
||||
instruction->dest_ptr = dest_ptr;
|
||||
instruction->byte = byte;
|
||||
instruction->count = count;
|
||||
|
||||
ir_ref_instruction(dest_ptr);
|
||||
ir_ref_instruction(byte);
|
||||
ir_ref_instruction(count);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_memset_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_memset(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, byte, count);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count)
|
||||
{
|
||||
IrInstructionMemcpy *instruction = ir_build_instruction<IrInstructionMemcpy>(irb, scope, source_node);
|
||||
instruction->dest_ptr = dest_ptr;
|
||||
instruction->src_ptr = src_ptr;
|
||||
instruction->count = count;
|
||||
|
||||
ir_ref_instruction(dest_ptr);
|
||||
ir_ref_instruction(src_ptr);
|
||||
ir_ref_instruction(count);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_memcpy_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_memcpy(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, src_ptr, count);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const)
|
||||
{
|
||||
IrInstructionSlice *instruction = ir_build_instruction<IrInstructionSlice>(irb, scope, source_node);
|
||||
instruction->ptr = ptr;
|
||||
instruction->start = start;
|
||||
instruction->end = end;
|
||||
instruction->is_const = is_const;
|
||||
|
||||
ir_ref_instruction(ptr);
|
||||
ir_ref_instruction(start);
|
||||
if (end) ir_ref_instruction(end);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_slice_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_slice(irb, old_instruction->scope, old_instruction->source_node, ptr, start, end, is_const);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
|
||||
bool gen_error_defers, bool gen_maybe_defers)
|
||||
{
|
||||
@ -2350,7 +2432,43 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdMemcpy:
|
||||
{
|
||||
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;
|
||||
|
||||
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
||||
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
||||
if (arg2_value == irb->codegen->invalid_instruction)
|
||||
return arg2_value;
|
||||
|
||||
return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
|
||||
}
|
||||
case BuiltinFnIdMemset:
|
||||
{
|
||||
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;
|
||||
|
||||
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
||||
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
||||
if (arg2_value == irb->codegen->invalid_instruction)
|
||||
return arg2_value;
|
||||
|
||||
return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
|
||||
}
|
||||
case BuiltinFnIdAlignof:
|
||||
case BuiltinFnIdMemberCount:
|
||||
case BuiltinFnIdAddWithOverflow:
|
||||
@ -3252,12 +3370,45 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode
|
||||
return ir_build_const_void(irb, parent_scope, node);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
assert(node->type == NodeTypeSliceExpr);
|
||||
|
||||
AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
|
||||
AstNode *array_node = slice_expr->array_ref_expr;
|
||||
AstNode *start_node = slice_expr->start;
|
||||
AstNode *end_node = slice_expr->end;
|
||||
|
||||
IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope);
|
||||
if (ptr_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *start_value = ir_gen_node(irb, start_node, scope);
|
||||
if (ptr_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *end_value;
|
||||
if (end_node) {
|
||||
end_value = ir_gen_node(irb, end_node, scope);
|
||||
if (end_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
} else {
|
||||
end_value = nullptr;
|
||||
}
|
||||
|
||||
return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, slice_expr->is_const);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
|
||||
LValPurpose lval)
|
||||
{
|
||||
assert(scope);
|
||||
switch (node->type) {
|
||||
case NodeTypeStructValueField:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeSwitchProng:
|
||||
case NodeTypeSwitchRange:
|
||||
zig_unreachable();
|
||||
case NodeTypeBlock:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval);
|
||||
@ -3319,21 +3470,17 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
|
||||
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval);
|
||||
case NodeTypeDefer:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval);
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeSliceExpr:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval);
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeCharLiteral:
|
||||
case NodeTypeZeroesLiteral:
|
||||
case NodeTypeVarLiteral:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeContainerDecl:
|
||||
case NodeTypeStructField:
|
||||
case NodeTypeSwitchProng:
|
||||
case NodeTypeSwitchRange:
|
||||
case NodeTypeErrorValueDecl:
|
||||
case NodeTypeTypeDecl:
|
||||
zig_panic("TODO more IR gen for node types");
|
||||
@ -4304,17 +4451,11 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst
|
||||
}
|
||||
|
||||
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
|
||||
if (handle_is_ptr(value->type_entry)) {
|
||||
// this instruction is a noop - codegen can pass the pointer we already have as the result
|
||||
ir_link_new_instruction(value, source_instruction);
|
||||
return ptr_type;
|
||||
} else {
|
||||
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
||||
assert(fn_entry);
|
||||
IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value);
|
||||
fn_entry->alloca_list.append(new_instruction);
|
||||
return ptr_type;
|
||||
}
|
||||
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
||||
assert(fn_entry);
|
||||
IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value);
|
||||
fn_entry->alloca_list.append(new_instruction);
|
||||
return ptr_type;
|
||||
}
|
||||
|
||||
|
||||
@ -7978,6 +8119,295 @@ static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructi
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
|
||||
IrInstruction *dest_ptr = instruction->dest_ptr->other;
|
||||
if (dest_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *byte_value = instruction->byte->other;
|
||||
if (byte_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *count_value = instruction->count->other;
|
||||
if (count_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
|
||||
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
|
||||
TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, u8, false);
|
||||
|
||||
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr);
|
||||
if (casted_dest_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8);
|
||||
if (casted_byte->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
|
||||
if (casted_count->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (casted_dest_ptr->static_value.special == ConstValSpecialStatic &&
|
||||
casted_byte->static_value.special == ConstValSpecialStatic &&
|
||||
casted_count->static_value.special == ConstValSpecialStatic)
|
||||
{
|
||||
ConstExprValue *dest_ptr_val = &casted_dest_ptr->static_value;
|
||||
|
||||
ConstExprValue *dest_elements;
|
||||
size_t start;
|
||||
size_t bound_end;
|
||||
if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) {
|
||||
dest_elements = dest_ptr_val->data.x_ptr.base_ptr;
|
||||
start = 0;
|
||||
bound_end = 1;
|
||||
} else {
|
||||
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr;
|
||||
dest_elements = array_val->data.x_array.elements;
|
||||
start = dest_ptr_val->data.x_ptr.index;
|
||||
bound_end = array_val->data.x_array.size;
|
||||
}
|
||||
|
||||
size_t count = casted_count->static_value.data.x_bignum.data.x_uint;
|
||||
size_t end = start + count;
|
||||
if (end > bound_end) {
|
||||
ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ConstExprValue *byte_val = &casted_byte->static_value;
|
||||
for (size_t i = start; i < end; i += 1) {
|
||||
dest_elements[i] = *byte_val;
|
||||
}
|
||||
|
||||
ir_build_const_from(ira, &instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
ir_build_memset_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_byte, casted_count);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) {
|
||||
IrInstruction *dest_ptr = instruction->dest_ptr->other;
|
||||
if (dest_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *src_ptr = instruction->src_ptr->other;
|
||||
if (src_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *count_value = instruction->count->other;
|
||||
if (count_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
|
||||
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
|
||||
TypeTableEntry *u8_ptr_mut = get_pointer_to_type(ira->codegen, u8, false);
|
||||
TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
|
||||
|
||||
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut);
|
||||
if (casted_dest_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const);
|
||||
if (casted_src_ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
|
||||
if (casted_count->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (casted_dest_ptr->static_value.special == ConstValSpecialStatic &&
|
||||
casted_src_ptr->static_value.special == ConstValSpecialStatic &&
|
||||
casted_count->static_value.special == ConstValSpecialStatic)
|
||||
{
|
||||
size_t count = casted_count->static_value.data.x_bignum.data.x_uint;
|
||||
|
||||
ConstExprValue *dest_ptr_val = &casted_dest_ptr->static_value;
|
||||
ConstExprValue *dest_elements;
|
||||
size_t dest_start;
|
||||
size_t dest_end;
|
||||
if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) {
|
||||
dest_elements = dest_ptr_val->data.x_ptr.base_ptr;
|
||||
dest_start = 0;
|
||||
dest_end = 1;
|
||||
} else {
|
||||
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr;
|
||||
dest_elements = array_val->data.x_array.elements;
|
||||
dest_start = dest_ptr_val->data.x_ptr.index;
|
||||
dest_end = array_val->data.x_array.size;
|
||||
}
|
||||
|
||||
if (dest_start + count > dest_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ConstExprValue *src_ptr_val = &casted_src_ptr->static_value;
|
||||
ConstExprValue *src_elements;
|
||||
size_t src_start;
|
||||
size_t src_end;
|
||||
if (src_ptr_val->data.x_ptr.index == SIZE_MAX) {
|
||||
src_elements = src_ptr_val->data.x_ptr.base_ptr;
|
||||
src_start = 0;
|
||||
src_end = 1;
|
||||
} else {
|
||||
ConstExprValue *array_val = src_ptr_val->data.x_ptr.base_ptr;
|
||||
src_elements = array_val->data.x_array.elements;
|
||||
src_start = src_ptr_val->data.x_ptr.index;
|
||||
src_end = array_val->data.x_array.size;
|
||||
}
|
||||
|
||||
if (src_start + count > src_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
// TODO check for noalias violations - this should be generalized to work for any function
|
||||
|
||||
for (size_t i = 0; i < count; i += 1) {
|
||||
dest_elements[dest_start + i] = src_elements[src_start + i];
|
||||
}
|
||||
|
||||
ir_build_const_from(ira, &instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
ir_build_memcpy_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_src_ptr, casted_count);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) {
|
||||
IrInstruction *ptr = instruction->ptr->other;
|
||||
if (ptr->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *start = instruction->start->other;
|
||||
if (start->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
|
||||
IrInstruction *casted_start = ir_implicit_cast(ira, start, usize);
|
||||
if (casted_start->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *end;
|
||||
if (instruction->end) {
|
||||
end = instruction->end->other;
|
||||
if (end->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
end = ir_implicit_cast(ira, end, usize);
|
||||
if (end->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
end = nullptr;
|
||||
}
|
||||
|
||||
TypeTableEntry *array_type = get_underlying_type(ptr->type_entry);
|
||||
|
||||
TypeTableEntry *return_type;
|
||||
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
return_type = get_slice_type(ira->codegen, array_type->data.array.child_type, instruction->is_const);
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
return_type = get_slice_type(ira->codegen, array_type->data.pointer.child_type, instruction->is_const);
|
||||
if (!end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
} else if (is_slice(array_type)) {
|
||||
return_type = get_slice_type(ira->codegen,
|
||||
array_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
|
||||
instruction->is_const);
|
||||
} else {
|
||||
ir_add_error(ira, &instruction->base,
|
||||
buf_sprintf("slice of non-array type '%s'", buf_ptr(&ptr->type_entry->name)));
|
||||
// TODO if this is a typedecl, add error note showing the declaration of the type decl
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
if (ptr->static_value.special == ConstValSpecialStatic &&
|
||||
casted_start->static_value.special == ConstValSpecialStatic &&
|
||||
(!end || end->static_value.special == ConstValSpecialStatic))
|
||||
{
|
||||
bool depends_on_compile_var =
|
||||
ptr->static_value.depends_on_compile_var ||
|
||||
casted_start->static_value.depends_on_compile_var ||
|
||||
(end ? end->static_value.depends_on_compile_var : false);
|
||||
|
||||
ConstExprValue *base_ptr;
|
||||
size_t abs_offset;
|
||||
size_t rel_end;
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
base_ptr = &ptr->static_value;
|
||||
abs_offset = 0;
|
||||
rel_end = array_type->data.array.len;
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
base_ptr = ptr->static_value.data.x_ptr.base_ptr;
|
||||
abs_offset = ptr->static_value.data.x_ptr.index;
|
||||
if (abs_offset == SIZE_MAX) {
|
||||
rel_end = 1;
|
||||
} else {
|
||||
rel_end = base_ptr->data.x_array.size - abs_offset;
|
||||
}
|
||||
} else if (is_slice(array_type)) {
|
||||
ConstExprValue *ptr_val = &ptr->static_value.data.x_struct.fields[slice_ptr_index];
|
||||
ConstExprValue *len_val = &ptr->static_value.data.x_struct.fields[slice_len_index];
|
||||
base_ptr = ptr_val->data.x_ptr.base_ptr;
|
||||
abs_offset = ptr_val->data.x_ptr.index;
|
||||
|
||||
if (ptr_val->data.x_ptr.index == SIZE_MAX) {
|
||||
rel_end = 1;
|
||||
} else {
|
||||
rel_end = len_val->data.x_bignum.data.x_uint;
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
uint64_t start_scalar = casted_start->static_value.data.x_bignum.data.x_uint;
|
||||
if (start_scalar > rel_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
uint64_t end_scalar;
|
||||
if (end) {
|
||||
end_scalar = end->static_value.data.x_bignum.data.x_uint;
|
||||
} else {
|
||||
end_scalar = rel_end;
|
||||
}
|
||||
if (end_scalar > rel_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
if (start_scalar > end_scalar) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
|
||||
out_val->data.x_struct.fields = allocate<ConstExprValue>(2);
|
||||
|
||||
ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index];
|
||||
ptr_val->special = ConstValSpecialStatic;
|
||||
ptr_val->data.x_ptr.base_ptr = base_ptr;
|
||||
ptr_val->data.x_ptr.index = (abs_offset != SIZE_MAX) ? (abs_offset + start_scalar) : SIZE_MAX;
|
||||
|
||||
ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
|
||||
len_val->special = ConstValSpecialStatic;
|
||||
bignum_init_unsigned(&len_val->data.x_bignum, rel_end);
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr, casted_start, end, instruction->is_const);
|
||||
ir_add_alloca(ira, new_instruction, return_type);
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
@ -8094,6 +8524,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdMemset:
|
||||
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
|
||||
case IrInstructionIdMemcpy:
|
||||
return ir_analyze_instruction_memcpy(ira, (IrInstructionMemcpy *)instruction);
|
||||
case IrInstructionIdSlice:
|
||||
return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction);
|
||||
case IrInstructionIdCast:
|
||||
case IrInstructionIdStructFieldPtr:
|
||||
case IrInstructionIdEnumFieldPtr:
|
||||
@ -8195,6 +8631,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdCUndef:
|
||||
case IrInstructionIdCmpxchg:
|
||||
case IrInstructionIdFence:
|
||||
case IrInstructionIdMemset:
|
||||
case IrInstructionIdMemcpy:
|
||||
return true;
|
||||
case IrInstructionIdPhi:
|
||||
case IrInstructionIdUnOp:
|
||||
@ -8236,6 +8674,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdIntType:
|
||||
case IrInstructionIdBoolNot:
|
||||
case IrInstructionIdAlloca:
|
||||
case IrInstructionIdSlice:
|
||||
return false;
|
||||
case IrInstructionIdAsm:
|
||||
{
|
||||
@ -8281,62 +8720,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
//
|
||||
// return g->builtin_types.entry_bool;
|
||||
// }
|
||||
// case BuiltinFnIdMemcpy:
|
||||
// {
|
||||
// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
|
||||
// AstNode *src_node = node->data.fn_call_expr.params.at(1);
|
||||
// AstNode *len_node = node->data.fn_call_expr.params.at(2);
|
||||
// TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
|
||||
// TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
|
||||
// analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
|
||||
//
|
||||
// if (dest_type->id != TypeTableEntryIdInvalid &&
|
||||
// dest_type->id != TypeTableEntryIdPointer)
|
||||
// {
|
||||
// add_node_error(g, dest_node,
|
||||
// buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&dest_type->name)));
|
||||
// }
|
||||
//
|
||||
// if (src_type->id != TypeTableEntryIdInvalid &&
|
||||
// src_type->id != TypeTableEntryIdPointer)
|
||||
// {
|
||||
// add_node_error(g, src_node,
|
||||
// buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&src_type->name)));
|
||||
// }
|
||||
//
|
||||
// if (dest_type->id == TypeTableEntryIdPointer &&
|
||||
// src_type->id == TypeTableEntryIdPointer)
|
||||
// {
|
||||
// uint64_t dest_align = get_memcpy_align(g, dest_type->data.pointer.child_type);
|
||||
// uint64_t src_align = get_memcpy_align(g, src_type->data.pointer.child_type);
|
||||
// if (dest_align != src_align) {
|
||||
// add_node_error(g, dest_node, buf_sprintf(
|
||||
// "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
|
||||
// buf_ptr(&dest_type->name), dest_align,
|
||||
// buf_ptr(&src_type->name), src_align));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return builtin_fn->return_type;
|
||||
// }
|
||||
// case BuiltinFnIdMemset:
|
||||
// {
|
||||
// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
|
||||
// AstNode *char_node = node->data.fn_call_expr.params.at(1);
|
||||
// AstNode *len_node = node->data.fn_call_expr.params.at(2);
|
||||
// TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
|
||||
// analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
|
||||
// analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
|
||||
//
|
||||
// if (dest_type->id != TypeTableEntryIdInvalid &&
|
||||
// dest_type->id != TypeTableEntryIdPointer)
|
||||
// {
|
||||
// add_node_error(g, dest_node,
|
||||
// buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&dest_type->name)));
|
||||
// }
|
||||
//
|
||||
// return builtin_fn->return_type;
|
||||
// }
|
||||
// case BuiltinFnIdAlignof:
|
||||
// {
|
||||
// AstNode *type_node = node->data.fn_call_expr.params.at(0);
|
||||
@ -8455,51 +8838,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// }
|
||||
// zig_unreachable();
|
||||
//}
|
||||
//static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
// AstNode *node)
|
||||
//{
|
||||
// assert(node->type == NodeTypeSliceExpr);
|
||||
//
|
||||
// TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
|
||||
// node->data.slice_expr.array_ref_expr);
|
||||
//
|
||||
// TypeTableEntry *return_type;
|
||||
//
|
||||
// if (array_type->id == TypeTableEntryIdInvalid) {
|
||||
// return_type = g->builtin_types.entry_invalid;
|
||||
// } else if (array_type->id == TypeTableEntryIdArray) {
|
||||
// return_type = get_slice_type(g, array_type->data.array.child_type,
|
||||
// node->data.slice_expr.is_const);
|
||||
// } else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
// return_type = get_slice_type(g, array_type->data.pointer.child_type,
|
||||
// node->data.slice_expr.is_const);
|
||||
// } else if (array_type->id == TypeTableEntryIdStruct &&
|
||||
// array_type->data.structure.is_slice)
|
||||
// {
|
||||
// return_type = get_slice_type(g,
|
||||
// array_type->data.structure.fields[0].type_entry->data.pointer.child_type,
|
||||
// node->data.slice_expr.is_const);
|
||||
// } else {
|
||||
// add_node_error(g, node,
|
||||
// buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
|
||||
// return_type = g->builtin_types.entry_invalid;
|
||||
// }
|
||||
//
|
||||
// if (return_type->id != TypeTableEntryIdInvalid) {
|
||||
// node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type;
|
||||
// node->data.slice_expr.resolved_struct_val_expr.source_node = node;
|
||||
// context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
|
||||
// }
|
||||
//
|
||||
// analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start);
|
||||
//
|
||||
// if (node->data.slice_expr.end) {
|
||||
// analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.end);
|
||||
// }
|
||||
//
|
||||
// return return_type;
|
||||
//}
|
||||
//
|
||||
//static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
// AstNode *node, LValPurpose purpose)
|
||||
//{
|
||||
@ -8656,65 +8994,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// }
|
||||
// case BuiltinFnIdShlWithOverflow:
|
||||
// return gen_shl_with_overflow(g, node);
|
||||
// case BuiltinFnIdMemcpy:
|
||||
// {
|
||||
// size_t fn_call_param_count = node->data.fn_call_expr.params.length;
|
||||
// assert(fn_call_param_count == 3);
|
||||
//
|
||||
// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
|
||||
// TypeTableEntry *dest_type = get_expr_type(dest_node);
|
||||
//
|
||||
// LLVMValueRef dest_ptr = gen_expr(g, dest_node);
|
||||
// LLVMValueRef src_ptr = gen_expr(g, node->data.fn_call_expr.params.at(1));
|
||||
// LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
|
||||
//
|
||||
// LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
//
|
||||
// LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
|
||||
// LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
|
||||
//
|
||||
// uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
|
||||
//
|
||||
// LLVMValueRef params[] = {
|
||||
// dest_ptr_casted, // dest pointer
|
||||
// src_ptr_casted, // source pointer
|
||||
// len_val, // byte count
|
||||
// LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
|
||||
// LLVMConstNull(LLVMInt1Type()), // is volatile
|
||||
// };
|
||||
//
|
||||
// LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
|
||||
// return nullptr;
|
||||
// }
|
||||
// case BuiltinFnIdMemset:
|
||||
// {
|
||||
// size_t fn_call_param_count = node->data.fn_call_expr.params.length;
|
||||
// assert(fn_call_param_count == 3);
|
||||
//
|
||||
// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
|
||||
// TypeTableEntry *dest_type = get_expr_type(dest_node);
|
||||
//
|
||||
// LLVMValueRef dest_ptr = gen_expr(g, dest_node);
|
||||
// LLVMValueRef char_val = gen_expr(g, node->data.fn_call_expr.params.at(1));
|
||||
// LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
|
||||
//
|
||||
// LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
//
|
||||
// LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
|
||||
//
|
||||
// uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
|
||||
//
|
||||
// LLVMValueRef params[] = {
|
||||
// dest_ptr_casted, // dest pointer
|
||||
// char_val, // source pointer
|
||||
// len_val, // byte count
|
||||
// LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
|
||||
// LLVMConstNull(LLVMInt1Type()), // is volatile
|
||||
// };
|
||||
//
|
||||
// LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
|
||||
// return nullptr;
|
||||
// }
|
||||
// case BuiltinFnIdAlignof:
|
||||
// case BuiltinFnIdMinValue:
|
||||
// case BuiltinFnIdMaxValue:
|
||||
@ -8789,25 +9068,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
|
||||
// TypeTableEntry *type_entry = get_expr_type(node);
|
||||
//
|
||||
// LLVMValueRef array_ptr;
|
||||
// if (node->type == NodeTypeFieldAccessExpr) {
|
||||
// array_ptr = gen_field_access_expr(g, node, true);
|
||||
// if (type_entry->id == TypeTableEntryIdPointer) {
|
||||
// // we have a double pointer so we must dereference it once
|
||||
// array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
|
||||
// }
|
||||
// } else {
|
||||
// array_ptr = gen_expr(g, node);
|
||||
// }
|
||||
//
|
||||
// assert(!array_ptr || LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
//
|
||||
// return array_ptr;
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
|
||||
// assert(node->type == NodeTypeArrayAccessExpr);
|
||||
//
|
||||
@ -8820,111 +9080,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
|
||||
// assert(node->type == NodeTypeSliceExpr);
|
||||
//
|
||||
// AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
|
||||
// TypeTableEntry *array_type = get_expr_type(array_ref_node);
|
||||
//
|
||||
// LLVMValueRef tmp_struct_ptr = node->data.slice_expr.resolved_struct_val_expr.ptr;
|
||||
// LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
|
||||
//
|
||||
// if (array_type->id == TypeTableEntryIdArray) {
|
||||
// LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
|
||||
// LLVMValueRef end_val;
|
||||
// if (node->data.slice_expr.end) {
|
||||
// end_val = gen_expr(g, node->data.slice_expr.end);
|
||||
// } else {
|
||||
// end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
|
||||
// }
|
||||
//
|
||||
// if (want_debug_safety(g, node)) {
|
||||
// add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
// if (node->data.slice_expr.end) {
|
||||
// LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||
// array_type->data.array.len, false);
|
||||
// add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
|
||||
// LLVMValueRef indices[] = {
|
||||
// LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
||||
// start_val,
|
||||
// };
|
||||
// LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
// LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
//
|
||||
// LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
|
||||
// LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
// LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
//
|
||||
// return tmp_struct_ptr;
|
||||
// } else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
// LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
|
||||
// LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
|
||||
//
|
||||
// if (want_debug_safety(g, node)) {
|
||||
// add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
// }
|
||||
//
|
||||
// LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
|
||||
// LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
|
||||
// LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
//
|
||||
// LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
|
||||
// LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
// LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
//
|
||||
// return tmp_struct_ptr;
|
||||
// } else if (array_type->id == TypeTableEntryIdStruct) {
|
||||
// assert(array_type->data.structure.is_slice);
|
||||
// assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
// assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
||||
//
|
||||
// size_t ptr_index = array_type->data.structure.fields[0].gen_index;
|
||||
// assert(ptr_index != SIZE_MAX);
|
||||
// size_t len_index = array_type->data.structure.fields[1].gen_index;
|
||||
// assert(len_index != SIZE_MAX);
|
||||
//
|
||||
// LLVMValueRef prev_end = nullptr;
|
||||
// if (!node->data.slice_expr.end || want_debug_safety(g, node)) {
|
||||
// LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
|
||||
// prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
|
||||
// }
|
||||
//
|
||||
// LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
|
||||
// LLVMValueRef end_val;
|
||||
// if (node->data.slice_expr.end) {
|
||||
// end_val = gen_expr(g, node->data.slice_expr.end);
|
||||
// } else {
|
||||
// end_val = prev_end;
|
||||
// }
|
||||
//
|
||||
// if (want_debug_safety(g, node)) {
|
||||
// assert(prev_end);
|
||||
// add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
// if (node->data.slice_expr.end) {
|
||||
// add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
|
||||
// LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
|
||||
// LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, "");
|
||||
// LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, len_index, "");
|
||||
// LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
|
||||
//
|
||||
// LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, len_index, "");
|
||||
// LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
// LLVMBuildStore(g->builder, len_value, len_field_ptr);
|
||||
//
|
||||
// return tmp_struct_ptr;
|
||||
// } else {
|
||||
// zig_unreachable();
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
|
||||
// assert(node->type == NodeTypeUnwrapErrorExpr);
|
||||
//
|
||||
|
@ -774,6 +774,38 @@ static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
}
|
||||
|
||||
static void ir_print_memset(IrPrint *irp, IrInstructionMemset *instruction) {
|
||||
fprintf(irp->f, "@memset(");
|
||||
ir_print_other_instruction(irp, instruction->dest_ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->byte);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->count);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_memcpy(IrPrint *irp, IrInstructionMemcpy *instruction) {
|
||||
fprintf(irp->f, "@memcpy(");
|
||||
ir_print_other_instruction(irp, instruction->dest_ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->src_ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->count);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, "[");
|
||||
ir_print_other_instruction(irp, instruction->start);
|
||||
fprintf(irp->f, "...");
|
||||
if (instruction->end)
|
||||
ir_print_other_instruction(irp, instruction->end);
|
||||
fprintf(irp->f, "]");
|
||||
if (instruction->is_const)
|
||||
fprintf(irp->f, "const");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
@ -959,6 +991,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdBoolNot:
|
||||
ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMemset:
|
||||
ir_print_memset(irp, (IrInstructionMemset *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMemcpy:
|
||||
ir_print_memcpy(irp, (IrInstructionMemcpy *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSlice:
|
||||
ir_print_slice(irp, (IrInstructionSlice *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
@ -44,8 +44,13 @@ pub struct Allocator {
|
||||
/// Copy all of source into dest at position 0.
|
||||
/// dest.len must be >= source.len.
|
||||
pub fn copy(inline T: type, dest: []T, source: []const T) {
|
||||
@setDebugSafety(this, false);
|
||||
assert(dest.len >= source.len);
|
||||
@memcpy(dest.ptr, source.ptr, @sizeOf(T) * source.len);
|
||||
for (source) |s, i| dest[i] = s;
|
||||
}
|
||||
|
||||
pub fn set(inline T: type, dest: []T, value: T) {
|
||||
for (dest) |*d| *d = value;
|
||||
}
|
||||
|
||||
/// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than,
|
||||
|
Loading…
Reference in New Issue
Block a user