IR: implement compileVar builtin and more

* implicit array to slice cast
 * fix if statements at global scope
 * implement array type IR
This commit is contained in:
Andrew Kelley
2016-11-19 01:39:51 -05:00
parent 19037014e5
commit 8a81f8aa13
7 changed files with 357 additions and 416 deletions

View File

@@ -526,240 +526,6 @@ static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddS
return result;
}
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, "");
}
}
}
static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
LLVMValueRef val1, LLVMValueRef val2,
TypeTableEntry *op1_type, TypeTableEntry *op2_type,
BinOpType bin_op)
{
assert(op1_type == op2_type);
switch (bin_op) {
case BinOpTypeBinOr:
case BinOpTypeAssignBitOr:
return LLVMBuildOr(g->builder, val1, val2, "");
case BinOpTypeBinXor:
case BinOpTypeAssignBitXor:
return LLVMBuildXor(g->builder, val1, val2, "");
case BinOpTypeBinAnd:
case BinOpTypeAssignBitAnd:
return LLVMBuildAnd(g->builder, val1, val2, "");
case BinOpTypeBitShiftLeft:
case BinOpTypeBitShiftLeftWrap:
case BinOpTypeAssignBitShiftLeft:
case BinOpTypeAssignBitShiftLeftWrap:
{
assert(op1_type->id == TypeTableEntryIdInt);
bool is_wrapping = (bin_op == BinOpTypeBitShiftLeftWrap) ||
(bin_op == BinOpTypeAssignBitShiftLeftWrap);
if (is_wrapping) {
return LLVMBuildShl(g->builder, val1, val2, "");
} else if (want_debug_safety(g, source_node)) {
return gen_overflow_shl_op(g, op1_type, val1, val2);
} else if (op1_type->data.integral.is_signed) {
return ZigLLVMBuildNSWShl(g->builder, val1, val2, "");
} else {
return ZigLLVMBuildNUWShl(g->builder, val1, val2, "");
}
}
case BinOpTypeBitShiftRight:
case BinOpTypeAssignBitShiftRight:
assert(op1_type->id == TypeTableEntryIdInt);
assert(op2_type->id == TypeTableEntryIdInt);
if (op1_type->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, val1, val2, "");
} else {
return LLVMBuildLShr(g->builder, val1, val2, "");
}
case BinOpTypeAdd:
case BinOpTypeAddWrap:
case BinOpTypeAssignPlus:
case BinOpTypeAssignPlusWrap:
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFAdd(g->builder, val1, val2, "");
} else if (op1_type->id == TypeTableEntryIdInt) {
bool is_wrapping = (bin_op == BinOpTypeAddWrap) || (bin_op == BinOpTypeAssignPlusWrap);
if (is_wrapping) {
return LLVMBuildAdd(g->builder, val1, val2, "");
} else if (want_debug_safety(g, source_node)) {
return gen_overflow_op(g, op1_type, AddSubMulAdd, val1, val2);
} else if (op1_type->data.integral.is_signed) {
return LLVMBuildNSWAdd(g->builder, val1, val2, "");
} else {
return LLVMBuildNUWAdd(g->builder, val1, val2, "");
}
} else {
zig_unreachable();
}
case BinOpTypeSub:
case BinOpTypeSubWrap:
case BinOpTypeAssignMinus:
case BinOpTypeAssignMinusWrap:
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFSub(g->builder, val1, val2, "");
} else if (op1_type->id == TypeTableEntryIdInt) {
bool is_wrapping = (bin_op == BinOpTypeSubWrap || bin_op == BinOpTypeAssignMinusWrap);
if (is_wrapping) {
return LLVMBuildSub(g->builder, val1, val2, "");
} else if (want_debug_safety(g, source_node)) {
return gen_overflow_op(g, op1_type, AddSubMulSub, val1, val2);
} else if (op1_type->data.integral.is_signed) {
return LLVMBuildNSWSub(g->builder, val1, val2, "");
} else {
return LLVMBuildNUWSub(g->builder, val1, val2, "");
}
} else {
zig_unreachable();
}
case BinOpTypeMult:
case BinOpTypeMultWrap:
case BinOpTypeAssignTimes:
case BinOpTypeAssignTimesWrap:
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFMul(g->builder, val1, val2, "");
} else if (op1_type->id == TypeTableEntryIdInt) {
bool is_wrapping = (bin_op == BinOpTypeMultWrap || bin_op == BinOpTypeAssignTimesWrap);
if (is_wrapping) {
return LLVMBuildMul(g->builder, val1, val2, "");
} else if (want_debug_safety(g, source_node)) {
return gen_overflow_op(g, op1_type, AddSubMulMul, val1, val2);
} else if (op1_type->data.integral.is_signed) {
return LLVMBuildNSWMul(g->builder, val1, val2, "");
} else {
return LLVMBuildNUWMul(g->builder, val1, val2, "");
}
} else {
zig_unreachable();
}
case BinOpTypeDiv:
case BinOpTypeAssignDiv:
return gen_div(g, source_node, val1, val2, op1_type, false);
case BinOpTypeMod:
case BinOpTypeAssignMod:
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFRem(g->builder, val1, val2, "");
} else {
assert(op1_type->id == TypeTableEntryIdInt);
if (op1_type->data.integral.is_signed) {
return LLVMBuildSRem(g->builder, val1, val2, "");
} else {
return LLVMBuildURem(g->builder, val1, val2, "");
}
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
case BinOpTypeCmpEq:
case BinOpTypeCmpNotEq:
case BinOpTypeCmpLessThan:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
case BinOpTypeInvalid:
case BinOpTypeAssign:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
case BinOpTypeUnwrapMaybe:
case BinOpTypeArrayCat:
case BinOpTypeArrayMult:
zig_unreachable();
}
zig_unreachable();
}
static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) {
switch (cmp_op) {
case IrBinOpCmpEq:
@@ -825,7 +591,7 @@ static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef
return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
}
static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node,
LLVMValueRef target_ref, LLVMValueRef value,
TypeTableEntry *op1_type, TypeTableEntry *op2_type)
{
@@ -834,18 +600,10 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
}
if (handle_is_ptr(op1_type)) {
assert(op1_type == op2_type);
assert(bin_op == BinOpTypeAssign);
return gen_struct_memcpy(g, value, target_ref, op1_type);
}
if (bin_op != BinOpTypeAssign) {
assert(source_node->type == NodeTypeBinOpExpr);
LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
}
LLVMBuildStore(g->builder, value, target_ref);
return nullptr;
}
@@ -1036,7 +794,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
return expr_val;
} else {
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
gen_assign_raw(g, cast_instruction->base.source_node,
val_ptr, expr_val, child_type, actual_type);
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
@@ -1065,7 +823,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
gen_assign_raw(g, cast_instruction->base.source_node,
payload_ptr, expr_val, child_type, actual_type);
return cast_instruction->tmp_ptr;
@@ -1414,7 +1172,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
want_zeroes = true;
if (have_init_expr) {
gen_assign_raw(g, init_value->source_node, BinOpTypeAssign, var->value_ref,
gen_assign_raw(g, init_value->source_node, var->value_ref,
ir_llvm_value(g, init_value), var->type, init_value->type_entry);
} else {
bool ignore_uninit = false;
@@ -1685,6 +1443,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdSetDebugSafety:
case IrInstructionIdArrayType:
case IrInstructionIdSliceType:
case IrInstructionIdCompileVar:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);