IR analysis unrolls a complicated loop

This commit is contained in:
Andrew Kelley 2016-10-27 03:28:29 -04:00
parent 78e6314422
commit 114049a220
4 changed files with 121 additions and 73 deletions

View File

@ -3575,6 +3575,7 @@ static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_
variable_entry->block_context = context;
variable_entry->import = import;
variable_entry->shadowable = shadowable;
variable_entry->mem_slot_index = SIZE_MAX;
if (name) {
buf_init_from_buf(&variable_entry->name, name);

View File

@ -1539,79 +1539,44 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
}
static LLVMIntPredicate cmp_op_to_int_predicate(BinOpType cmp_op, bool is_signed) {
static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) {
switch (cmp_op) {
case BinOpTypeCmpEq:
case IrBinOpCmpEq:
return LLVMIntEQ;
case BinOpTypeCmpNotEq:
case IrBinOpCmpNotEq:
return LLVMIntNE;
case BinOpTypeCmpLessThan:
case IrBinOpCmpLessThan:
return is_signed ? LLVMIntSLT : LLVMIntULT;
case BinOpTypeCmpGreaterThan:
case IrBinOpCmpGreaterThan:
return is_signed ? LLVMIntSGT : LLVMIntUGT;
case BinOpTypeCmpLessOrEq:
case IrBinOpCmpLessOrEq:
return is_signed ? LLVMIntSLE : LLVMIntULE;
case BinOpTypeCmpGreaterOrEq:
case IrBinOpCmpGreaterOrEq:
return is_signed ? LLVMIntSGE : LLVMIntUGE;
default:
zig_unreachable();
}
}
static LLVMRealPredicate cmp_op_to_real_predicate(BinOpType cmp_op) {
static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) {
switch (cmp_op) {
case BinOpTypeCmpEq:
case IrBinOpCmpEq:
return LLVMRealOEQ;
case BinOpTypeCmpNotEq:
case IrBinOpCmpNotEq:
return LLVMRealONE;
case BinOpTypeCmpLessThan:
case IrBinOpCmpLessThan:
return LLVMRealOLT;
case BinOpTypeCmpGreaterThan:
case IrBinOpCmpGreaterThan:
return LLVMRealOGT;
case BinOpTypeCmpLessOrEq:
case IrBinOpCmpLessOrEq:
return LLVMRealOLE;
case BinOpTypeCmpGreaterOrEq:
case IrBinOpCmpGreaterOrEq:
return LLVMRealOGE;
default:
zig_unreachable();
}
}
static LLVMValueRef gen_cmp_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);
assert(op1_type == op2_type);
if (op1_type->id == TypeTableEntryIdFloat) {
LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op);
return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
} else if (op1_type->id == TypeTableEntryIdInt) {
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op,
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(node->data.bin_op_expr.bin_op, false);
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
} else {
zig_unreachable();
}
} else if (op1_type->id == TypeTableEntryIdPureError ||
op1_type->id == TypeTableEntryIdPointer ||
op1_type->id == TypeTableEntryIdBool)
{
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op, false);
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
} else {
zig_unreachable();
}
}
static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
@ -1844,7 +1809,7 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
return gen_cmp_expr(g, node);
zig_panic("moved to ir_render");
case BinOpTypeUnwrapMaybe:
return gen_unwrap_maybe_expr(g, node);
case BinOpTypeBinOr:
@ -2341,6 +2306,41 @@ static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable,
}
}
static LLVMValueRef ir_render_bin_op_cmp(CodeGen *g, IrExecutable *executable,
IrInstructionBinOp *bin_op_instruction)
{
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, "");
} 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();
}
}
static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable,
IrInstructionBinOp *bin_op_instruction)
{
@ -2389,7 +2389,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
zig_panic("TODO bin op cmp");
return ir_render_bin_op_cmp(g, executable, bin_op_instruction);
case IrBinOpAdd:
case IrBinOpAddWrap:
return ir_render_bin_op_add(g, executable, bin_op_instruction);
@ -2860,6 +2860,13 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildLoad(g->builder, ir_llvm_value(g, instruction->ptr), "");
}
static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
LLVMValueRef value = ir_llvm_value(g, instruction->value);
LLVMBuildStore(g->builder, value, ptr);
return nullptr;
}
static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
return instruction->var->value_ref;
}
@ -2931,13 +2938,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction);
case IrInstructionIdLoadPtr:
return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction);
case IrInstructionIdStorePtr:
return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction);
case IrInstructionIdVarPtr:
return ir_render_var_ptr(g, executable, (IrInstructionVarPtr *)instruction);
case IrInstructionIdCall:
return ir_render_call(g, executable, (IrInstructionCall *)instruction);
case IrInstructionIdSwitchBr:
case IrInstructionIdPhi:
case IrInstructionIdStorePtr:
case IrInstructionIdBuiltinCall:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:

View File

@ -21,7 +21,7 @@ struct IrAnalyze {
IrBuilder old_irb;
IrBuilder new_irb;
IrExecContext exec_context;
ZigList<IrBasicBlock *> block_queue;
ZigList<IrBasicBlock *> old_bb_queue;
size_t block_queue_index;
size_t instruction_index;
TypeTableEntry *explicit_return_type;
@ -1609,15 +1609,15 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) {
if (old_bb->other)
return old_bb->other;
IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb);
ira->block_queue.append(new_bb);
ira->old_bb_queue.append(old_bb);
return new_bb;
}
static void ir_finish_bb(IrAnalyze *ira) {
ira->block_queue_index += 1;
if (ira->block_queue_index < ira->block_queue.length) {
IrBasicBlock *old_bb = ira->block_queue.at(ira->block_queue_index);
if (ira->block_queue_index < ira->old_bb_queue.length) {
IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index);
ira->instruction_index = 0;
ira->new_irb.current_basic_block = ir_get_new_bb(ira, old_bb);
ira->old_irb.current_basic_block = old_bb;
@ -2260,9 +2260,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
return ira->codegen->builtin_types.entry_invalid;
}
if (op1->static_value.ok && op2->static_value.ok) {
ConstExprValue *op1_val = &op1->static_value;
ConstExprValue *op2_val = &op2->static_value;
IrInstruction *casted_op1 = ir_get_casted_value(ira, op1, resolved_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_get_casted_value(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
if (casted_op1->static_value.ok && casted_op2->static_value.ok) {
ConstExprValue *op1_val = &casted_op1->static_value;
ConstExprValue *op2_val = &casted_op2->static_value;
ConstExprValue *out_val = &bin_op_instruction->base.static_value;
bin_op_instruction->base.other = &bin_op_instruction->base;
@ -2286,8 +2295,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
}
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1, op2);
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, casted_op1, casted_op2);
return resolved_type;
}
@ -4006,17 +4014,20 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
VariableTableEntry *var = var_ptr_instruction->var;
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var_ptr_instruction->var->type, false);
if (mem_slot->ok) {
ConstExprValue *out_val = ir_get_out_val(&var_ptr_instruction->base);
// TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing.
if (var->mem_slot_index != SIZE_MAX) {
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
if (mem_slot->ok) {
ConstExprValue *out_val = ir_get_out_val(&var_ptr_instruction->base);
out_val->ok = true;
out_val->data.x_ptr.len = 1;
out_val->data.x_ptr.is_c_str = false;
out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1);
out_val->data.x_ptr.ptr[0] = mem_slot;
return ptr_type;
out_val->ok = true;
out_val->data.x_ptr.len = 1;
out_val->data.x_ptr.is_c_str = false;
out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1);
out_val->data.x_ptr.ptr[0] = mem_slot;
return ptr_type;
}
}
ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var);
@ -4065,6 +4076,30 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru
}
}
if (ptr->static_value.ok) {
// This memory location is transforming from known at compile time to known at runtime.
// We must emit our own var ptr instruction.
ptr->static_value.ok = false;
IrInstruction *new_ptr_inst;
if (ptr->id == IrInstructionIdVarPtr) {
IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr;
VariableTableEntry *var = var_ptr_inst->var;
new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, var);
assert(var->mem_slot_index != SIZE_MAX);
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
mem_slot->ok = false;
} else if (ptr->id == IrInstructionIdFieldPtr) {
zig_panic("TODO");
} else if (ptr->id == IrInstructionIdElemPtr) {
zig_panic("TODO");
} else {
zig_unreachable();
}
new_ptr_inst->type_entry = ptr->type_entry;
ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value);
return ira->codegen->builtin_types.entry_void;
}
ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value);
return ira->codegen->builtin_types.entry_void;
}
@ -4153,7 +4188,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
ira->block_queue_index = 0;
ira->instruction_index = 0;
while (ira->block_queue_index < ira->block_queue.length) {
while (ira->block_queue_index < ira->old_bb_queue.length) {
IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) {
ira->instruction_index += 1;

View File

@ -93,11 +93,15 @@ static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction)
ir_print_const_value(irp, type_entry, const_val);
}
static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
fprintf(irp->f, "#%zu", instruction->debug_id);
}
static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
if (instruction->static_value.ok) {
ir_print_const_instruction(irp, instruction);
} else {
fprintf(irp->f, "#%zu", instruction->debug_id);
ir_print_var_instruction(irp, instruction);
}
}
@ -333,7 +337,7 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) {
static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) {
fprintf(irp->f, "*");
ir_print_other_instruction(irp, instruction->ptr);
ir_print_var_instruction(irp, instruction->ptr);
fprintf(irp->f, " = ");
ir_print_other_instruction(irp, instruction->value);
}