Commit ef63bc9cca
Changed files (7)
doc/langref.md
@@ -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
src/all_types.hpp
@@ -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,
src/ast_render.cpp
@@ -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:
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();
}
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);
//
src/ir_print.cpp
@@ -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");
}
std/mem.zig
@@ -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,