IR: fix codegen for arrays
parent
e80e8a8099
commit
052cd44588
|
@ -1626,6 +1626,7 @@ struct IrInstructionElemPtr {
|
|||
IrInstruction *array_ptr;
|
||||
IrInstruction *elem_index;
|
||||
bool is_const;
|
||||
bool safety_check_on;
|
||||
};
|
||||
|
||||
struct IrInstructionVarPtr {
|
||||
|
|
352
src/codegen.cpp
352
src/codegen.cpp
|
@ -455,55 +455,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
|
|||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr,
|
||||
TypeTableEntry *array_type, LLVMValueRef subscript_value)
|
||||
{
|
||||
assert(subscript_value);
|
||||
|
||||
if (!type_has_bits(array_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
if (want_debug_safety(g, source_node)) {
|
||||
LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||
array_type->data.array.len, false);
|
||||
add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end);
|
||||
}
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
||||
subscript_value
|
||||
};
|
||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
LLVMValueRef indices[] = {
|
||||
subscript_value
|
||||
};
|
||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
|
||||
} 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);
|
||||
|
||||
if (want_debug_safety(g, source_node)) {
|
||||
size_t len_index = array_type->data.structure.fields[1].gen_index;
|
||||
assert(len_index != SIZE_MAX);
|
||||
LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
|
||||
LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, "");
|
||||
add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
|
||||
}
|
||||
|
||||
size_t ptr_index = array_type->data.structure.fields[0].gen_index;
|
||||
assert(ptr_index != SIZE_MAX);
|
||||
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
|
||||
LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, "");
|
||||
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddSubMul op,
|
||||
LLVMValueRef val1, LLVMValueRef val2)
|
||||
{
|
||||
|
@ -640,83 +591,98 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionBinOp *bin_op_instruction)
|
||||
static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
|
||||
LLVMValueRef val1, LLVMValueRef val2)
|
||||
{
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
LLVMValueRef op1 = ir_llvm_value(g, bin_op_instruction->op1);
|
||||
LLVMValueRef op2 = ir_llvm_value(g, bin_op_instruction->op2);
|
||||
if (op_id == IrBinOpBoolOr) {
|
||||
return LLVMBuildOr(g->builder, op1, op2, "");
|
||||
} else if (op_id == IrBinOpBoolAnd) {
|
||||
return LLVMBuildAnd(g->builder, op1, op2, "");
|
||||
// for unsigned left shifting, we do the wrapping shift, then logically shift
|
||||
// right the same number of bits
|
||||
// if the values don't match, we have an overflow
|
||||
// for signed left shifting we do the same except arithmetic shift right
|
||||
|
||||
assert(type_entry->id == TypeTableEntryIdInt);
|
||||
|
||||
LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
|
||||
LLVMValueRef orig_val;
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
orig_val = LLVMBuildAShr(g->builder, result, val2, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
orig_val = LLVMBuildLShr(g->builder, result, val2, "");
|
||||
}
|
||||
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
|
||||
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
return result;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_bin_op_cmp(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionBinOp *bin_op_instruction)
|
||||
static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
|
||||
TypeTableEntry *type_entry, bool exact)
|
||||
{
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
LLVMValueRef val1 = ir_llvm_value(g, bin_op_instruction->op1);
|
||||
LLVMValueRef val2 = ir_llvm_value(g, bin_op_instruction->op2);
|
||||
|
||||
TypeTableEntry *op1_type = bin_op_instruction->op1->type_entry;
|
||||
TypeTableEntry *op2_type = bin_op_instruction->op2->type_entry;
|
||||
assert(op1_type == op2_type);
|
||||
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
|
||||
return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
|
||||
} else if (op1_type->id == TypeTableEntryIdInt) {
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, op1_type->data.integral.is_signed);
|
||||
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
|
||||
} else if (op1_type->id == TypeTableEntryIdEnum) {
|
||||
if (op1_type->data.enumeration.gen_field_count == 0) {
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
|
||||
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
|
||||
if (want_debug_safety(g, source_node)) {
|
||||
LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
LLVMValueRef is_zero_bit;
|
||||
if (type_entry->id == TypeTableEntryIdInt) {
|
||||
is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||
} else if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
} else if (op1_type->id == TypeTableEntryIdPureError ||
|
||||
op1_type->id == TypeTableEntryIdPointer ||
|
||||
op1_type->id == TypeTableEntryIdBool)
|
||||
{
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
|
||||
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
|
||||
LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionBinOp *bin_op_instruction)
|
||||
{
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
IrInstruction *op1 = bin_op_instruction->op1;
|
||||
IrInstruction *op2 = bin_op_instruction->op2;
|
||||
if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
assert(!exact);
|
||||
return LLVMBuildFDiv(g->builder, val1, val2, "");
|
||||
}
|
||||
|
||||
assert(op1->type_entry == op2->type_entry);
|
||||
assert(type_entry->id == TypeTableEntryIdInt);
|
||||
|
||||
LLVMValueRef op1_value = ir_llvm_value(g, op1);
|
||||
LLVMValueRef op2_value = ir_llvm_value(g, op2);
|
||||
if (exact) {
|
||||
if (want_debug_safety(g, source_node)) {
|
||||
LLVMValueRef remainder_val;
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
|
||||
} else {
|
||||
remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
|
||||
}
|
||||
LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
|
||||
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (ir_want_debug_safety(g, &bin_op_instruction->base)) {
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail");
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildExactSDiv(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
||||
return ZigLLVMBuildExactUDiv(g->builder, val1, val2, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildSDiv(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildUDiv(g->builder, val1, val2, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,37 +690,146 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
|||
IrInstructionBinOp *bin_op_instruction)
|
||||
{
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
IrInstruction *op1 = bin_op_instruction->op1;
|
||||
IrInstruction *op2 = bin_op_instruction->op2;
|
||||
AstNode *source_node = bin_op_instruction->base.source_node;
|
||||
|
||||
assert(op1->type_entry == op2->type_entry);
|
||||
|
||||
LLVMValueRef op1_value = ir_llvm_value(g, op1);
|
||||
LLVMValueRef op2_value = ir_llvm_value(g, op2);
|
||||
switch (op_id) {
|
||||
case IrBinOpInvalid:
|
||||
case IrBinOpArrayCat:
|
||||
case IrBinOpArrayMult:
|
||||
zig_unreachable();
|
||||
case IrBinOpBoolOr:
|
||||
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
|
||||
case IrBinOpBoolAnd:
|
||||
return ir_render_bin_op_bool(g, executable, bin_op_instruction);
|
||||
return LLVMBuildAnd(g->builder, op1_value, op2_value, "");
|
||||
case IrBinOpCmpEq:
|
||||
case IrBinOpCmpNotEq:
|
||||
case IrBinOpCmpLessThan:
|
||||
case IrBinOpCmpGreaterThan:
|
||||
case IrBinOpCmpLessOrEq:
|
||||
case IrBinOpCmpGreaterOrEq:
|
||||
return ir_render_bin_op_cmp(g, executable, bin_op_instruction);
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
|
||||
return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, op1->type_entry->data.integral.is_signed);
|
||||
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdEnum) {
|
||||
if (op1->type_entry->data.enumeration.gen_field_count == 0) {
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
|
||||
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdPureError ||
|
||||
op1->type_entry->id == TypeTableEntryIdPointer ||
|
||||
op1->type_entry->id == TypeTableEntryIdBool)
|
||||
{
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
|
||||
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpAdd:
|
||||
case IrBinOpAddWrap:
|
||||
return ir_render_bin_op_add(g, executable, bin_op_instruction);
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (ir_want_debug_safety(g, &bin_op_instruction->base)) {
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpBinOr:
|
||||
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
|
||||
case IrBinOpBinXor:
|
||||
return LLVMBuildXor(g->builder, op1_value, op2_value, "");
|
||||
case IrBinOpBinAnd:
|
||||
return LLVMBuildAnd(g->builder, op1_value, op2_value, "");
|
||||
case IrBinOpBitShiftLeft:
|
||||
case IrBinOpBitShiftLeftWrap:
|
||||
{
|
||||
assert(op1->type_entry->id == TypeTableEntryIdInt);
|
||||
bool is_wrapping = (op_id == IrBinOpBitShiftLeftWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildShl(g->builder, op1_value, op2_value, "");
|
||||
} else if (want_debug_safety(g, source_node)) {
|
||||
return gen_overflow_shl_op(g, op1->type_entry, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
}
|
||||
case IrBinOpBitShiftRight:
|
||||
assert(op1->type_entry->id == TypeTableEntryIdInt);
|
||||
if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildAShr(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildLShr(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
case IrBinOpSub:
|
||||
case IrBinOpSubWrap:
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpSubWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildSub(g->builder, op1_value, op2_value, "");
|
||||
} else if (want_debug_safety(g, source_node)) {
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulSub, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpMult:
|
||||
case IrBinOpMultWrap:
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpMultWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildMul(g->builder, op1_value, op2_value, "");
|
||||
} else if (want_debug_safety(g, source_node)) {
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulMul, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpDiv:
|
||||
return gen_div(g, source_node, op1_value, op2_value, op1->type_entry, false);
|
||||
case IrBinOpMod:
|
||||
zig_panic("TODO render more bin ops to LLVM");
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFRem(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
assert(op1->type_entry->id == TypeTableEntryIdInt);
|
||||
if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildSRem(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildURem(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -1242,10 +1317,56 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn
|
|||
|
||||
static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrInstructionElemPtr *instruction) {
|
||||
LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr);
|
||||
LLVMValueRef array_ptr = LLVMBuildLoad(g->builder, array_ptr_ptr, "");
|
||||
TypeTableEntry *array_ptr_type = instruction->array_ptr->type_entry;
|
||||
assert(array_ptr_type->id == TypeTableEntryIdPointer);
|
||||
TypeTableEntry *array_type = array_ptr_type->data.pointer.child_type;
|
||||
LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type);
|
||||
LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index);
|
||||
TypeTableEntry *array_type = instruction->array_ptr->type_entry;
|
||||
return gen_array_elem_ptr(g, instruction->base.source_node, array_ptr, array_type, subscript_value);
|
||||
assert(subscript_value);
|
||||
|
||||
if (!type_has_bits(array_type))
|
||||
return nullptr;
|
||||
|
||||
bool safety_check_on = ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on;
|
||||
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
if (safety_check_on) {
|
||||
LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||
array_type->data.array.len, false);
|
||||
add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end);
|
||||
}
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
||||
subscript_value
|
||||
};
|
||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
LLVMValueRef indices[] = {
|
||||
subscript_value
|
||||
};
|
||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
|
||||
} 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);
|
||||
|
||||
if (safety_check_on) {
|
||||
size_t len_index = array_type->data.structure.fields[1].gen_index;
|
||||
assert(len_index != SIZE_MAX);
|
||||
LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
|
||||
LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, "");
|
||||
add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
|
||||
}
|
||||
|
||||
size_t ptr_index = array_type->data.structure.fields[0].gen_index;
|
||||
assert(ptr_index != SIZE_MAX);
|
||||
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
|
||||
LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, "");
|
||||
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) {
|
||||
|
@ -1423,7 +1544,6 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
|
|||
LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),
|
||||
buf_ptr(&constraint_buf), is_volatile, false);
|
||||
|
||||
set_debug_source_node(g, asm_node);
|
||||
return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
|
||||
}
|
||||
|
||||
|
|
124
src/ir.cpp
124
src/ir.cpp
|
@ -506,11 +506,12 @@ static IrInstruction *ir_build_var_ptr_from(IrBuilder *irb, IrInstruction *old_i
|
|||
}
|
||||
|
||||
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, AstNode *source_node, IrInstruction *array_ptr,
|
||||
IrInstruction *elem_index)
|
||||
IrInstruction *elem_index, bool safety_check_on)
|
||||
{
|
||||
IrInstructionElemPtr *instruction = ir_build_instruction<IrInstructionElemPtr>(irb, source_node);
|
||||
instruction->array_ptr = array_ptr;
|
||||
instruction->elem_index = elem_index;
|
||||
instruction->safety_check_on = safety_check_on;
|
||||
|
||||
ir_ref_instruction(array_ptr);
|
||||
ir_ref_instruction(elem_index);
|
||||
|
@ -519,9 +520,10 @@ static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, AstNode *source_node, Ir
|
|||
}
|
||||
|
||||
static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *array_ptr, IrInstruction *elem_index)
|
||||
IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->source_node, array_ptr, elem_index);
|
||||
IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->source_node, array_ptr, elem_index,
|
||||
safety_check_on);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
@ -1353,7 +1355,8 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, AstNode *node, LValPur
|
|||
if (subscript_instruction == irb->codegen->invalid_instruction)
|
||||
return subscript_instruction;
|
||||
|
||||
IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, node, array_ref_instruction, subscript_instruction);
|
||||
IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, node, array_ref_instruction,
|
||||
subscript_instruction, true);
|
||||
if (lval != LValPurposeNone)
|
||||
return ptr_instruction;
|
||||
|
||||
|
@ -1840,7 +1843,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) {
|
|||
ir_build_cond_br(irb, node, cond, body_block, end_block, is_inline);
|
||||
|
||||
ir_set_cursor_at_end(irb, body_block);
|
||||
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, node, array_val, index_val);
|
||||
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, node, array_val, index_val, true);
|
||||
IrInstruction *elem_val;
|
||||
if (node->data.for_expr.elem_is_ptr) {
|
||||
elem_val = elem_ptr;
|
||||
|
@ -3960,6 +3963,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
|
|||
if (casted_elem_index == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
bool safety_check_on = true;
|
||||
if (casted_elem_index->static_value.special != ConstValSpecialRuntime) {
|
||||
uint64_t index = casted_elem_index->static_value.data.x_bignum.data.x_uint;
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
|
@ -3970,6 +3974,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
|
|||
index, array_len));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
safety_check_on = false;
|
||||
}
|
||||
|
||||
ConstExprValue *array_ptr_val;
|
||||
|
@ -4031,7 +4036,8 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
|
|||
|
||||
}
|
||||
|
||||
ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index);
|
||||
ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr,
|
||||
casted_elem_index, safety_check_on);
|
||||
return return_type;
|
||||
}
|
||||
|
||||
|
@ -8668,18 +8674,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
|
|||
// return target_ref;
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
|
||||
// assert(node->type == NodeTypeBinOpExpr);
|
||||
//
|
||||
// LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
|
||||
// LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
|
||||
//
|
||||
// TypeTableEntry *op1_type = get_expr_type(node->data.bin_op_expr.op1);
|
||||
// TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
|
||||
// return gen_arithmetic_bin_op(g, node, val1, val2, op1_type, op2_type, node->data.bin_op_expr.bin_op);
|
||||
//
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
|
||||
// assert(node->type == NodeTypeBinOpExpr);
|
||||
//
|
||||
|
@ -9678,97 +9672,3 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
|
|||
// LLVMPositionBuilderAtEnd(g->builder, basic_block);
|
||||
// return nullptr;
|
||||
//}
|
||||
//static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
|
||||
// LLVMValueRef val1, LLVMValueRef val2)
|
||||
//{
|
||||
// // for unsigned left shifting, we do the wrapping shift, then logically shift
|
||||
// // right the same number of bits
|
||||
// // if the values don't match, we have an overflow
|
||||
// // for signed left shifting we do the same except arithmetic shift right
|
||||
//
|
||||
// assert(type_entry->id == TypeTableEntryIdInt);
|
||||
//
|
||||
// LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
|
||||
// LLVMValueRef orig_val;
|
||||
// if (type_entry->data.integral.is_signed) {
|
||||
// orig_val = LLVMBuildAShr(g->builder, result, val2, "");
|
||||
// } else {
|
||||
// orig_val = LLVMBuildLShr(g->builder, result, val2, "");
|
||||
// }
|
||||
// LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
|
||||
//
|
||||
// LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
|
||||
// LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
|
||||
// LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
// gen_debug_safety_crash(g);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
// return result;
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
|
||||
// TypeTableEntry *type_entry, bool exact)
|
||||
//{
|
||||
//
|
||||
// if (want_debug_safety(g, source_node)) {
|
||||
// LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
// LLVMValueRef is_zero_bit;
|
||||
// if (type_entry->id == TypeTableEntryIdInt) {
|
||||
// is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||
// } else if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
// is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
|
||||
// } else {
|
||||
// zig_unreachable();
|
||||
// }
|
||||
// LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
|
||||
// LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
|
||||
// LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
// gen_debug_safety_crash(g);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
// }
|
||||
//
|
||||
// if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
// assert(!exact);
|
||||
// return LLVMBuildFDiv(g->builder, val1, val2, "");
|
||||
// }
|
||||
//
|
||||
// assert(type_entry->id == TypeTableEntryIdInt);
|
||||
//
|
||||
// if (exact) {
|
||||
// if (want_debug_safety(g, source_node)) {
|
||||
// LLVMValueRef remainder_val;
|
||||
// if (type_entry->data.integral.is_signed) {
|
||||
// remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
|
||||
// } else {
|
||||
// remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
|
||||
// }
|
||||
// LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
// LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
|
||||
//
|
||||
// LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk");
|
||||
// LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail");
|
||||
// LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
// gen_debug_safety_crash(g);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
// }
|
||||
// if (type_entry->data.integral.is_signed) {
|
||||
// return LLVMBuildExactSDiv(g->builder, val1, val2, "");
|
||||
// } else {
|
||||
// return ZigLLVMBuildExactUDiv(g->builder, val1, val2, "");
|
||||
// }
|
||||
// } else {
|
||||
// if (type_entry->data.integral.is_signed) {
|
||||
// return LLVMBuildSDiv(g->builder, val1, val2, "");
|
||||
// } else {
|
||||
// return LLVMBuildUDiv(g->builder, val1, val2, "");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -359,6 +359,9 @@ static void ir_print_elem_ptr(IrPrint *irp, IrInstructionElemPtr *instruction) {
|
|||
fprintf(irp->f, "[");
|
||||
ir_print_other_instruction(irp, instruction->elem_index);
|
||||
fprintf(irp->f, "]");
|
||||
if (!instruction->safety_check_on) {
|
||||
fprintf(irp->f, " // no safety");
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) {
|
||||
|
|
Loading…
Reference in New Issue