mirror of
https://github.com/ziglang/zig.git
synced 2026-02-04 21:53:38 +00:00
IR analysis unrolls a complicated loop
This commit is contained in:
parent
78e6314422
commit
114049a220
@ -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);
|
||||
|
||||
112
src/codegen.cpp
112
src/codegen.cpp
@ -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:
|
||||
|
||||
73
src/ir.cpp
73
src/ir.cpp
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user