forked from M-Labs/nac3
core: Simplify loop condition check for list comprehension
This commit is contained in:
parent
0205161e35
commit
50230e61f3
|
@ -3,6 +3,7 @@ use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||||
|
gen_in_range_check,
|
||||||
get_llvm_type,
|
get_llvm_type,
|
||||||
get_llvm_abi_type,
|
get_llvm_abi_type,
|
||||||
irrt::*,
|
irrt::*,
|
||||||
|
@ -963,11 +964,14 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
||||||
|
|
||||||
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?;
|
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?;
|
||||||
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
|
||||||
|
ctx.builder.build_conditional_branch(
|
||||||
|
gen_in_range_check(ctx, start, stop, step),
|
||||||
|
test_bb,
|
||||||
|
cont_bb,
|
||||||
|
);
|
||||||
|
|
||||||
ctx.builder.position_at_end(test_bb);
|
ctx.builder.position_at_end(test_bb);
|
||||||
let sign =
|
|
||||||
ctx.builder.build_int_compare(IntPredicate::SGT, step, zero_32, "sign");
|
|
||||||
// add and test
|
// add and test
|
||||||
let tmp = ctx.builder.build_int_add(
|
let tmp = ctx.builder.build_int_add(
|
||||||
ctx.builder.build_load(i, "i").into_int_value(),
|
ctx.builder.build_load(i, "i").into_int_value(),
|
||||||
|
@ -975,14 +979,8 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
||||||
"start_loop",
|
"start_loop",
|
||||||
);
|
);
|
||||||
ctx.builder.build_store(i, tmp);
|
ctx.builder.build_store(i, tmp);
|
||||||
// if step > 0, continue when i < end
|
|
||||||
let cmp1 = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, stop, "cmp1");
|
|
||||||
// if step < 0, continue when i > end
|
|
||||||
let cmp2 = ctx.builder.build_int_compare(IntPredicate::SGT, tmp, stop, "cmp2");
|
|
||||||
let pos = ctx.builder.build_and(sign, cmp1, "pos");
|
|
||||||
let neg = ctx.builder.build_and(ctx.builder.build_not(sign, "inv"), cmp2, "neg");
|
|
||||||
ctx.builder.build_conditional_branch(
|
ctx.builder.build_conditional_branch(
|
||||||
ctx.builder.build_or(pos, neg, "or"),
|
gen_in_range_check(ctx, tmp, stop, step),
|
||||||
body_bb,
|
body_bb,
|
||||||
cont_bb,
|
cont_bb,
|
||||||
);
|
);
|
||||||
|
@ -1001,7 +999,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
||||||
ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content")).into_pointer_value();
|
ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content")).into_pointer_value();
|
||||||
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
||||||
// counter = -1
|
// counter = -1
|
||||||
ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true));
|
ctx.builder.build_store(counter, size_t.const_int(u64::MAX, true));
|
||||||
ctx.builder.build_unconditional_branch(test_bb);
|
ctx.builder.build_unconditional_branch(test_bb);
|
||||||
|
|
||||||
ctx.builder.position_at_end(test_bb);
|
ctx.builder.position_at_end(test_bb);
|
||||||
|
|
|
@ -899,3 +899,31 @@ fn bool_to_i8<'ctx>(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a sequence of IR which checks whether `value` does not exceed the upper bound of the
|
||||||
|
/// range as defined by `stop` and `step`.
|
||||||
|
///
|
||||||
|
/// Note that the generated IR will **not** check whether value is part of the range or whether
|
||||||
|
/// value exceeds the lower bound of the range (as evident by the missing `start` argument).
|
||||||
|
///
|
||||||
|
/// The generated IR is equivalent to the following Rust code:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let sign = step > 0;
|
||||||
|
/// let (lo, hi) = if sign { (value, stop) } else { (stop, value) };
|
||||||
|
/// let cmp = lo < hi;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Returns an `i1` [IntValue] representing the result of whether the `value` is in the range.
|
||||||
|
fn gen_in_range_check<'ctx, 'a>(
|
||||||
|
ctx: &CodeGenContext<'ctx, 'a>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
stop: IntValue<'ctx>,
|
||||||
|
step: IntValue<'ctx>,
|
||||||
|
) -> IntValue<'ctx> {
|
||||||
|
let sign = ctx.builder.build_int_compare(IntPredicate::SGT, step, ctx.ctx.i32_type().const_zero(), "");
|
||||||
|
let lo = ctx.builder.build_select(sign, value, stop, "").into_int_value();
|
||||||
|
let hi = ctx.builder.build_select(sign, stop, value, "").into_int_value();
|
||||||
|
|
||||||
|
ctx.builder.build_int_compare(IntPredicate::SLT, lo, hi, "cmp")
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@ use super::{
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::expr::gen_binop_expr,
|
codegen::{
|
||||||
|
expr::gen_binop_expr,
|
||||||
|
gen_in_range_check,
|
||||||
|
},
|
||||||
toplevel::{DefinitionId, TopLevelDef},
|
toplevel::{DefinitionId, TopLevelDef},
|
||||||
typecheck::typedef::{FunSignature, Type, TypeEnum},
|
typecheck::typedef::{FunSignature, Type, TypeEnum},
|
||||||
};
|
};
|
||||||
|
@ -13,7 +16,7 @@ use inkwell::{
|
||||||
attributes::{Attribute, AttributeLoc},
|
attributes::{Attribute, AttributeLoc},
|
||||||
basic_block::BasicBlock,
|
basic_block::BasicBlock,
|
||||||
types::BasicTypeEnum,
|
types::BasicTypeEnum,
|
||||||
values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue},
|
values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue},
|
||||||
IntPredicate,
|
IntPredicate,
|
||||||
};
|
};
|
||||||
use nac3parser::ast::{
|
use nac3parser::ast::{
|
||||||
|
@ -232,34 +235,6 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a sequence of IR which checks whether `value` does not exceed the upper bound of the
|
|
||||||
/// range as defined by `stop` and `step`.
|
|
||||||
///
|
|
||||||
/// Note that the generated IR will **not** check whether value is part of the range or whether
|
|
||||||
/// value exceeds the lower bound of the range (as evident by the missing `start` argument).
|
|
||||||
///
|
|
||||||
/// The generated IR is equivalent to the following Rust code:
|
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// let sign = step > 0;
|
|
||||||
/// let (lo, hi) = if sign { (value, stop) } else { (stop, value) };
|
|
||||||
/// let cmp = lo < hi;
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Returns an `i1` [IntValue] representing the result of whether the `value` is in the range.
|
|
||||||
fn gen_in_range_check<'ctx, 'a>(
|
|
||||||
ctx: &CodeGenContext<'ctx, 'a>,
|
|
||||||
value: IntValue<'ctx>,
|
|
||||||
stop: IntValue<'ctx>,
|
|
||||||
step: IntValue<'ctx>,
|
|
||||||
) -> IntValue<'ctx> {
|
|
||||||
let sign = ctx.builder.build_int_compare(IntPredicate::SGT, step, ctx.ctx.i32_type().const_zero(), "");
|
|
||||||
let lo = ctx.builder.build_select(sign, value, stop, "").into_int_value();
|
|
||||||
let hi = ctx.builder.build_select(sign, stop, value, "").into_int_value();
|
|
||||||
|
|
||||||
ctx.builder.build_int_compare(IntPredicate::SLT, lo, hi, "cmp")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [CodeGenerator::gen_for].
|
/// See [CodeGenerator::gen_for].
|
||||||
pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
|
|
Loading…
Reference in New Issue