summaryrefslogtreecommitdiff
path: root/src/mir/from_hir_match.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/from_hir_match.cpp')
-rw-r--r--src/mir/from_hir_match.cpp2983
1 files changed, 1214 insertions, 1769 deletions
diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp
index b098f900..ec720d8a 100644
--- a/src/mir/from_hir_match.cpp
+++ b/src/mir/from_hir_match.cpp
@@ -33,8 +33,6 @@ struct field_path_t
};
TAGGED_UNION_EX(PatternRule, (), Any,(
- // _ pattern
- (Any, struct {}),
// Enum variant
(Variant, struct { unsigned int idx; ::std::vector<PatternRule> sub_rules; }),
// Slice (includes desired length)
@@ -46,11 +44,24 @@ TAGGED_UNION_EX(PatternRule, (), Any,(
(Bool, bool),
// General value
(Value, ::MIR::Constant),
- (ValueRange, struct { ::MIR::Constant first, last; })
+ (ValueRange, struct { ::MIR::Constant first, last; }),
+ // _ pattern
+ (Any, struct {})
),
( , field_path(mv$(x.field_path)) ), (field_path = mv$(x.field_path);),
(
field_path_t field_path;
+
+ bool operator<(const PatternRule& x) const {
+ return this->ord(x) == OrdLess;
+ }
+ bool operator==(const PatternRule& x) const {
+ return this->ord(x) == OrdEqual;
+ }
+ bool operator!=(const PatternRule& x) const {
+ return this->ord(x) != OrdEqual;
+ }
+ Ordering ord(const PatternRule& x) const;
)
);
::std::ostream& operator<<(::std::ostream& os, const PatternRule& x);
@@ -74,11 +85,14 @@ struct ArmCode {
::MIR::BasicBlockId cond_end;
::MIR::LValue cond_lval;
::std::vector< ::MIR::BasicBlockId> destructures; // NOTE: Incomplete
+
+ mutable ::MIR::BasicBlockId cond_fail_tgt = 0;
};
typedef ::std::vector<PatternRuleset> t_arm_rules;
void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector<ArmCode> arm_code, ::MIR::BasicBlockId first_cmp_block);
+void MIR_LowerHIR_Match_Grouped( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector<ArmCode> arms_code, ::MIR::BasicBlockId first_cmp_block );
void MIR_LowerHIR_Match_DecisionTree( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector<ArmCode> arm_code , ::MIR::BasicBlockId first_cmp_block);
/// Helper to construct rules from a passed pattern
struct PatternRulesetBuilder
@@ -103,6 +117,82 @@ struct PatternRulesetBuilder
void push_rule(PatternRule r);
};
+class RulesetRef
+{
+ ::std::vector<PatternRuleset>* m_rules_vec = nullptr;
+ RulesetRef* m_parent = nullptr;
+ size_t m_parent_ofs=0; // If len == 0, this is the innner index, else it's the base
+ size_t m_parent_len=0;
+public:
+ RulesetRef(::std::vector<PatternRuleset>& rules):
+ m_rules_vec(&rules)
+ {
+ }
+ RulesetRef(RulesetRef& parent, size_t start, size_t n):
+ m_parent(&parent),
+ m_parent_ofs(start),
+ m_parent_len(n)
+ {
+ }
+ RulesetRef(RulesetRef& parent, size_t idx):
+ m_parent(&parent),
+ m_parent_ofs(idx)
+ {
+ }
+
+ size_t size() const {
+ if( m_rules_vec ) {
+ return m_rules_vec->size();
+ }
+ else if( m_parent_len ) {
+ return m_parent_len;
+ }
+ else {
+ return m_parent->size();
+ }
+ }
+ RulesetRef slice(size_t s, size_t n) {
+ return RulesetRef(*this, s, n);
+ }
+
+ const ::std::vector<PatternRule>& operator[](size_t i) const {
+ if( m_rules_vec ) {
+ return (*m_rules_vec)[i].m_rules;
+ }
+ else if( m_parent_len ) {
+ return (*m_parent)[m_parent_ofs + i];
+ }
+ else {
+ // Fun part - Indexes into inner patterns
+ const auto& parent_rule = (*m_parent)[i][m_parent_ofs];
+ if(const auto* re = parent_rule.opt_Variant()) {
+ return re->sub_rules;
+ }
+ else {
+ throw "TODO";
+ }
+ }
+ }
+ void swap(size_t a, size_t b) {
+ TRACE_FUNCTION_F(a << ", " << b);
+ if( m_rules_vec ) {
+ ::std::swap( (*m_rules_vec)[a], (*m_rules_vec)[b] );
+ }
+ else {
+ assert(m_parent);
+ if( m_parent_len ) {
+ m_parent->swap(m_parent_ofs + a, m_parent_ofs + b);
+ }
+ else {
+ m_parent->swap(a, b);
+ }
+ }
+ }
+};
+
+void sort_rulesets(RulesetRef rulesets, size_t idx=0);
+void sort_rulesets_inner(RulesetRef rulesets, size_t idx);
+
// --------------------------------------------------------------------
// CODE
// --------------------------------------------------------------------
@@ -282,8 +372,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
auto tmp_scope = builder.new_scope_temp(arm.m_cond->span());
conv.visit_node_ptr( arm.m_cond );
- ac.cond_lval = builder.get_result_in_lvalue(arm.m_cond->span(), ::HIR::TypeRef(::HIR::CoreType::Bool));
- // NOTE: Terminating the scope slightly early is safe, because the resulting boolean temp isn't invalidated.
+ ac.cond_lval = builder.get_result_in_if_cond(arm.m_cond->span());
builder.terminate_scope( arm.m_code->span(), mv$(tmp_scope) );
ac.cond_end = builder.pause_cur_block();
@@ -366,20 +455,55 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
for(const auto& arm_rule : arm_rules)
{
- DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules);
+ DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules
+ << (arm_code[arm_rule.arm_idx].has_condition ? " (cond)" : ""));
}
- // TODO: Don't generate inner code until decisions are generated (keeps MIR flow nice)
+ // TODO: Remove columns that are all `_`?
+ // - Ideally, only accessible structures would be fully destructured like this, making this check redundant
+
+ // Sort rules using the following restrictions:
+ // - A rule cannot be reordered across an item that has an overlapping match set
+ // > e.g. nothing can cross _
+ // > equal rules cannot be reordered
+ // > Values cannot cross ranges that contain the value
+ // > This will have to be a bubble sort to ensure that it's correctly stable.
+ sort_rulesets(arm_rules);
+ DEBUG("Post-sort");
+ for(const auto& arm_rule : arm_rules)
+ {
+ DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules
+ << (arm_code[arm_rule.arm_idx].has_condition ? " (cond)" : ""));
+ }
+ // De-duplicate arms (emitting a warning when it happens)
+ // - This allows later code to assume that duplicate arms are a codegen bug.
+ if( ! arm_rules.empty() )
+ {
+ for(auto it = arm_rules.begin()+1; it != arm_rules.end(); )
+ {
+ // If duplicate rule, (and neither is conditional)
+ if( (it-1)->m_rules == it->m_rules && !arm_code[it->arm_idx].has_condition && !arm_code[(it-1)->arm_idx].has_condition )
+ {
+ // Remove
+ it = arm_rules.erase(it);
+ WARNING(node.m_arms[it->arm_idx].m_code->span(), W0000, "Duplicate match pattern, unreachable code");
+ }
+ else
+ {
+ ++ it;
+ }
+ }
+ }
- // TODO: Detect if a rule is ordering-dependent. In this case we currently have to fall back on the simple match code
- // - A way would be to search for `_` rules with non _ rules following. Would false-positive in some cases, but shouldn't false negative
- // TODO: Merge equal rulesets if there's one with no condition.
+ // TODO: Don't generate inner code until decisions are generated (keeps MIR flow nice)
+ // - Challenging, as the decision code needs somewhere to jump to.
+ // - Allocating a BB and then rewriting references to it is a possibility.
if( fall_back_on_simple ) {
MIR_LowerHIR_Match_Simple( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block );
}
else {
- MIR_LowerHIR_Match_DecisionTree( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block );
+ MIR_LowerHIR_Match_Grouped( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block );
}
builder.set_cur_block( next_block );
@@ -424,6 +548,56 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
return os;
}
+::Ordering PatternRule::ord(const PatternRule& x) const
+{
+ if(tag() != x.tag())
+ {
+ return tag() < x.tag() ? ::OrdLess : ::OrdGreater;
+ }
+ TU_MATCHA( (*this, x), (te, xe),
+ (Any, return OrdEqual;),
+ (Variant,
+ if(te.idx != xe.idx) return ::ord(te.idx, xe.idx);
+ assert( te.sub_rules.size() == xe.sub_rules.size() );
+ for(unsigned int i = 0; i < te.sub_rules.size(); i ++)
+ {
+ auto cmp = te.sub_rules[i].ord( xe.sub_rules[i] );
+ if( cmp != ::OrdEqual )
+ return cmp;
+ }
+ return ::OrdEqual;
+ ),
+ (Slice,
+ if(te.len != xe.len) return ::ord(te.len, xe.len);
+ // Wait? Why would the rule count be the same?
+ assert( te.sub_rules.size() == xe.sub_rules.size() );
+ for(unsigned int i = 0; i < te.sub_rules.size(); i ++)
+ {
+ auto cmp = te.sub_rules[i].ord( xe.sub_rules[i] );
+ if( cmp != ::OrdEqual )
+ return cmp;
+ }
+ return ::OrdEqual;
+ ),
+ (SplitSlice,
+ auto rv = ::ord( te.leading, xe.leading );
+ if(rv != OrdEqual) return rv;
+ return ::ord(te.trailing, xe.trailing);
+ ),
+ (Bool,
+ return ::ord( te, xe );
+ ),
+ (Value,
+ return ::ord( te, xe );
+ ),
+ (ValueRange,
+ if( te.first != xe.first )
+ return ::ord(te.first, xe.first);
+ return ::ord(te.last, xe.last);
+ )
+ )
+ throw "";
+}
::Ordering PatternRuleset::rule_is_before(const PatternRule& l, const PatternRule& r)
{
if( l.tag() != r.tag() ) {
@@ -1212,6 +1386,7 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa
sub_builder.m_field_path.back() = 0;
if( pe.trailing.size() )
{
+ // Needs a way of encoding the negative offset in the field path
TODO(sp, "SplitSlice on [T] with trailing - " << pat);
}
auto trailing = mv$(sub_builder.m_rules);
@@ -1281,6 +1456,227 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa
}
namespace {
+ // Order rules ignoring inner rules
+ Ordering ord_rule_compatible(const PatternRule& a, const PatternRule& b)
+ {
+ if(a.tag() != b.tag())
+ return ::ord( (unsigned)a.tag(), b.tag() );
+
+ TU_MATCHA( (a, b), (ae, be),
+ (Any,
+ return OrdEqual;
+ ),
+ (Variant,
+ return ::ord(ae.idx, be.idx);
+ ),
+ (Slice,
+ return ::ord(ae.len, be.len);
+ ),
+ (SplitSlice,
+ auto v = ::ord(ae.leading.size(), be.leading.size());
+ if(v != OrdEqual) return v;
+ v = ::ord(ae.trailing.size(), be.trailing.size());
+ if(v != OrdEqual) return v;
+ return OrdEqual;
+ ),
+ (Bool,
+ return ::ord(ae, be);
+ ),
+ (Value,
+ return ::ord(ae, be);
+ ),
+ (ValueRange,
+ auto v = ::ord(ae.first, be.first);
+ if(v != OrdEqual) return v;
+ return ::ord(ae.last, be.last);
+ )
+ )
+ throw "";
+ }
+ bool rule_compatible(const PatternRule& a, const PatternRule& b)
+ {
+ return ord_rule_compatible(a,b) == OrdEqual;
+ }
+
+ bool rules_overlap(const PatternRule& a, const PatternRule& b)
+ {
+ if( a.is_Any() || b.is_Any() )
+ return true;
+
+ // Defensive: If a constant is encountered, assume it overlaps with anything
+ if(const auto* ae = a.opt_Value()) {
+ if(ae->is_Const())
+ return true;
+ }
+ if(const auto* be = b.opt_Value()) {
+ if(be->is_Const())
+ return true;
+ }
+
+ // Value Range: Overlaps with contained values.
+ if(const auto* ae = a.opt_ValueRange() )
+ {
+ if(const auto* be = b.opt_Value() )
+ {
+ return ( ae->first <= *be && *be <= ae->last );
+ }
+ else if( const auto* be = b.opt_ValueRange() )
+ {
+ // Start of B within A
+ if( ae->first <= be->first && be->first <= ae->last )
+ return true;
+ // End of B within A
+ if( ae->first <= be->last && be->last <= ae->last )
+ return true;
+ // Start of A within B
+ if( be->first <= ae->first && ae->first <= be->last )
+ return true;
+ // End of A within B
+ if( be->first <= ae->last && ae->last <= be->last )
+ return true;
+
+ // Disjoint
+ return false;
+ }
+ else
+ {
+ TODO(Span(), "Check overlap of " << a << " and " << b);
+ }
+ }
+ if(const auto* be = b.opt_ValueRange())
+ {
+ if(const auto* ae = a.opt_Value() )
+ {
+ return (be->first <= *ae && *ae <= be->last);
+ }
+ // Note: A can't be ValueRange
+ else
+ {
+ TODO(Span(), "Check overlap of " << a << " and " << b);
+ }
+ }
+
+ // SplitSlice patterns overlap with other SplitSlice patterns and larger slices
+ if(const auto* ae = a.opt_SplitSlice())
+ {
+ if( b.is_SplitSlice() )
+ {
+ return true;
+ }
+ else if( const auto* be = b.opt_Slice() )
+ {
+ return be->len >= ae->min_len;
+ }
+ else
+ {
+ TODO(Span(), "Check overlap of " << a << " and " << b);
+ }
+ }
+ if(const auto* be = b.opt_SplitSlice())
+ {
+ if( const auto* ae = a.opt_Slice() )
+ {
+ return ae->len >= be->min_len;
+ }
+ else
+ {
+ TODO(Span(), "Check overlap of " << a << " and " << b);
+ }
+ }
+
+ // Otherwise, If rules are approximately equal, they overlap
+ return ( ord_rule_compatible(a, b) == OrdEqual );
+ }
+}
+void sort_rulesets(RulesetRef rulesets, size_t idx)
+{
+ if(rulesets.size() < 2)
+ return ;
+
+ bool found_non_any = false;
+ for(size_t i = 0; i < rulesets.size(); i ++)
+ if( !rulesets[i][idx].is_Any() )
+ found_non_any = true;
+ if( found_non_any )
+ {
+ TRACE_FUNCTION_F(idx);
+ for(size_t i = 0; i < rulesets.size(); i ++)
+ DEBUG("- " << i << ": " << rulesets[i]);
+
+ bool action_taken;
+ do
+ {
+ action_taken = false;
+ for(size_t i = 0; i < rulesets.size()-1; i ++)
+ {
+ if( rules_overlap(rulesets[i][idx], rulesets[i+1][idx]) )
+ {
+ // Don't move
+ }
+ else if( ord_rule_compatible(rulesets[i][idx], rulesets[i+1][idx]) == OrdGreater )
+ {
+ rulesets.swap(i, i+1);
+ action_taken = true;
+ }
+ else
+ {
+ }
+ }
+ } while(action_taken);
+ for(size_t i = 0; i < rulesets.size(); i ++)
+ DEBUG("- " << i << ": " << rulesets[i]);
+
+ // TODO: Print sorted ruleset
+
+ // Where compatible, sort insides
+ size_t start = 0;
+ for(size_t i = 1; i < rulesets.size(); i++)
+ {
+ if( ord_rule_compatible(rulesets[i][idx], rulesets[start][idx]) != OrdEqual )
+ {
+ sort_rulesets_inner(rulesets.slice(start, i-start), idx);
+ start = i;
+ }
+ }
+ sort_rulesets_inner(rulesets.slice(start, rulesets.size()-start), idx);
+
+ // Iterate onwards where rules are equal
+ if( idx + 1 < rulesets[0].size() )
+ {
+ size_t start = 0;
+ for(size_t i = 1; i < rulesets.size(); i++)
+ {
+ if( rulesets[i][idx] != rulesets[start][idx] )
+ {
+ sort_rulesets(rulesets.slice(start, i-start), idx+1);
+ start = i;
+ }
+ }
+ sort_rulesets(rulesets.slice(start, rulesets.size()-start), idx+1);
+ }
+ }
+ else
+ {
+ if( idx + 1 < rulesets[0].size() )
+ {
+ sort_rulesets(rulesets, idx + 1);
+ }
+ }
+}
+void sort_rulesets_inner(RulesetRef rulesets, size_t idx)
+{
+ TRACE_FUNCTION_F(idx << " - " << rulesets[0][idx].tag_str());
+ if( const auto* re = rulesets[0][idx].opt_Variant() )
+ {
+ // Sort rules based on contents of enum
+ if( re->sub_rules.size() > 0 )
+ {
+ sort_rulesets(RulesetRef(rulesets, idx), 0);
+ }
+ }
+}
+
+namespace {
void get_ty_and_val(
const Span& sp, const StaticTraitResolve& resolve,
const ::HIR::TypeRef& top_ty, const ::MIR::LValue& top_val,
@@ -1437,7 +1833,6 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::
TRACE_FUNCTION;
// 1. Generate pattern matches
- unsigned int rule_idx = 0;
builder.set_cur_block( first_cmp_block );
for( unsigned int arm_idx = 0; arm_idx < node.m_arms.size(); arm_idx ++ )
{
@@ -1451,6 +1846,10 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::
if( arm_code.destructures[i] == 0 )
continue ;
+ size_t rule_idx = 0;
+ for(; rule_idx < arm_rules.size(); rule_idx++)
+ if( arm_rules[rule_idx].arm_idx == arm_idx && arm_rules[rule_idx].pat_idx == i )
+ break;
const auto& pat_rule = arm_rules[rule_idx];
bool is_last_pat = (i+1 == arm.m_patterns.size());
auto next_pattern_bb = (!is_last_pat ? builder.new_bb_unlinked() : next_arm_bb);
@@ -1478,8 +1877,6 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::
{
builder.set_cur_block( next_pattern_bb );
}
-
- rule_idx ++;
}
if( arm_code.has_condition )
{
@@ -1549,8 +1946,8 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span&
auto succ_bb = builder.new_bb_unlinked();
auto test_val = ::MIR::Param( ::MIR::Constant::make_Uint({ re.as_Uint().v, te }));
- auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) }));
- builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), succ_bb, fail_bb }) );
+ builder.push_stmt_assign(sp, builder.get_if_cond(), ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) }));
+ builder.end_block( ::MIR::Terminator::make_If({ builder.get_if_cond(), succ_bb, fail_bb }) );
builder.set_cur_block(succ_bb);
),
(ValueRange,
@@ -1649,7 +2046,38 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span&
break;
case ::HIR::CoreType::F32:
case ::HIR::CoreType::F64:
- TODO(sp, "Simple match over float - " << ty);
+ TU_MATCH_DEF( PatternRule, (rule), (re),
+ (
+ BUG(sp, "PatternRule for float is not Value or ValueRange");
+ ),
+ (Value,
+ auto succ_bb = builder.new_bb_unlinked();
+
+ auto test_val = ::MIR::Param(::MIR::Constant::make_Float({ re.as_Float().v, te }));
+ auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) }));
+ builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), succ_bb, fail_bb }) );
+ builder.set_cur_block(succ_bb);
+ ),
+ (ValueRange,
+ auto succ_bb = builder.new_bb_unlinked();
+ auto test_bb_2 = builder.new_bb_unlinked();
+
+ auto test_lt_val = ::MIR::Param(::MIR::Constant::make_Float({ re.first.as_Float().v, te }));
+ auto test_gt_val = ::MIR::Param(::MIR::Constant::make_Float({ re.last.as_Float().v, te }));
+
+ // IF `val` < `first` : fail_bb
+ auto cmp_lt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, mv$(test_lt_val) }));
+ builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), fail_bb, test_bb_2 }) );
+
+ builder.set_cur_block(test_bb_2);
+
+ // IF `val` > `last` : fail_bb
+ auto cmp_gt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, mv$(test_gt_val) }));
+ builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), fail_bb, succ_bb }) );
+
+ builder.set_cur_block(succ_bb);
+ )
+ )
break;
case ::HIR::CoreType::Str: {
ASSERT_BUG(sp, rule.is_Value() && rule.as_Value().is_StaticString(), "");
@@ -1690,7 +2118,11 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span&
TODO(sp, "Match over Union");
),
(Enum,
- auto monomorph = [&](const auto& ty) { return monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty); };
+ auto monomorph = [&](const auto& ty) {
+ auto rv = monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty);
+ builder.resolve().expand_associated_types(sp, rv);
+ return rv;
+ };
ASSERT_BUG(sp, rule.is_Variant(), "Rule for enum isn't Any or Variant");
const auto& re = rule.as_Variant();
unsigned int var_idx = re.idx;
@@ -1865,1958 +2297,971 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span&
return 0;
}
-// --------------------------------------------------------------------
-// Decision Tree
-// --------------------------------------------------------------------
+// --
+// Match v2 Algo - Grouped rules
+// --
-// ## Create descision tree in-memory based off the ruleset
-// > Tree contains an lvalue and a set of possibilities (PatternRule) connected to another tree or to a branch index
-struct DecisionTreeNode
+
+class t_rules_subset
{
- TAGGED_UNION( Branch, Unset,
- (Unset, struct{}),
- (Subtree, ::std::unique_ptr<DecisionTreeNode>),
- (Terminal, unsigned int)
- );
-
- template<typename T>
- struct Range
+ ::std::vector<const ::std::vector<PatternRule>*> rule_sets;
+ bool is_arm_indexes;
+ ::std::vector<size_t> arm_idxes;
+public:
+ t_rules_subset(size_t exp, bool is_arm_indexes):
+ is_arm_indexes(is_arm_indexes)
{
- T start;
- T end;
-
- // `x` starts after this range ends
- bool operator<(const Range<T>& x) const {
- return (end < x.start);
- }
- // `x` falls above the end of this range
- bool operator<(const T& x) const {
- return (end < x);
- }
-
- // `x` ends before this starts, or overlaps
- bool operator>=(const Range<T>& x) const {
- return start > x.end || ovelaps(x);
- }
- // `x` is before or within this range
- bool operator>=(const T& x) const {
- return start > x || contains(x);
- }
+ rule_sets.reserve(exp);
+ arm_idxes.reserve(exp);
+ }
- bool operator>(const Range<T>& x) const {
- return (start > x.end);
- }
- bool operator>(const T& x) const {
- return (start > x);
- }
+ size_t size() const {
+ return rule_sets.size();
+ }
+ const ::std::vector<PatternRule>& operator[](size_t n) const {
+ return *rule_sets[n];
+ }
+ bool is_arm() const { return is_arm_indexes; }
+ ::std::pair<size_t,size_t> arm_idx(size_t n) const {
+ assert(is_arm_indexes);
+ auto v = arm_idxes.at(n);
+ return ::std::make_pair(v & 0xFFF, v >> 12);
+ }
+ ::MIR::BasicBlockId bb_idx(size_t n) const {
+ assert(!is_arm_indexes);
+ return arm_idxes.at(n);
+ }
- bool operator==(const Range<T>& x) const {
- return start == x.start && end == x.end;
- }
- bool operator!=(const Range<T>& x) const {
- return start != x.start || end != x.end;
- }
+ void sub_sort(size_t ofs, size_t start, size_t n)
+ {
+ ::std::vector<size_t> v;
+ for(size_t i = 0; i < n; i++)
+ v.push_back(start + i);
+ // Sort rules based on just the value (ignore inner rules)
+ ::std::stable_sort( v.begin(), v.end(), [&](auto a, auto b){ return ord_rule_compatible( (*rule_sets[a])[ofs], (*rule_sets[b])[ofs]) == OrdLess; } );
- bool contains(const T& x) const {
- return (start <= x && x <= end);
+ // Reorder contents to above sorting
+ {
+ decltype(this->rule_sets) tmp;
+ for(auto i : v)
+ tmp.push_back(rule_sets[i]);
+ ::std::copy( tmp.begin(), tmp.end(), rule_sets.begin() + start );
}
- bool overlaps(const Range<T>& x) const {
- return (x.start <= start && start <= x.end) || (x.start <= end && end <= x.end);
+ {
+ decltype(this->arm_idxes) tmp;
+ for(auto i : v)
+ tmp.push_back(arm_idxes[i]);
+ ::std::copy( tmp.begin(), tmp.end(), arm_idxes.begin() + start );
}
+ }
- friend ::std::ostream& operator<<(::std::ostream& os, const Range<T>& x) {
- if( x.start == x.end ) {
- return os << x.start;
- }
- else {
- return os << x.start << " ... " << x.end;
- }
+ t_rules_subset sub_slice(size_t ofs, size_t n)
+ {
+ t_rules_subset rv { n, this->is_arm_indexes };
+ rv.rule_sets.reserve(n);
+ for(size_t i = 0; i < n; i++)
+ {
+ rv.rule_sets.push_back( this->rule_sets[ofs+i] );
+ rv.arm_idxes.push_back( this->arm_idxes[ofs+i] );
}
- };
-
- TAGGED_UNION( Values, Unset,
- (Unset, struct {}),
- (Bool, struct { Branch false_branch, true_branch; }),
- (Variant, ::std::vector< ::std::pair<unsigned int, Branch> >),
- (Unsigned, ::std::vector< ::std::pair< Range<uint64_t>, Branch> >),
- (Signed, ::std::vector< ::std::pair< Range<int64_t>, Branch> >),
- (Float, ::std::vector< ::std::pair< Range<double>, Branch> >),
- (String, ::std::vector< ::std::pair< ::std::string, Branch> >),
- (Slice, struct {
- ::std::vector< ::std::pair< unsigned int, Branch> > fixed_arms;
- //::std::vector< ::std::pair< unsigned int, Branch> > variable_arms;
- })
- );
-
- // TODO: Arm specialisation?
- field_path_t m_field_path;
- Values m_branches;
- Branch m_default;
-
- DecisionTreeNode( field_path_t field_path ):
- // TODO: This is commented out fo a reason, but I don't know why.
- //m_field_path( mv$(field_path) ),
- m_branches(),
- m_default()
- {}
-
- static Branch clone(const Branch& b);
- static Values clone(const Values& x);
- DecisionTreeNode clone() const;
-
- void populate_tree_from_rule(const Span& sp, unsigned int arm_index, const PatternRule* first_rule, unsigned int rule_count) {
- populate_tree_from_rule(sp, first_rule, rule_count, [sp,arm_index](auto& branch){
- TU_MATCHA( (branch), (e),
- (Unset,
- // Good
- ),
- (Subtree,
- if( e->m_branches.is_Unset() && e->m_default.is_Unset() ) {
- // Good.
- }
- else {
- BUG(sp, "Duplicate terminal - branch="<<branch);
- }
- ),
- (Terminal,
- // TODO: This is ok if it's due to overlapping rules (e.g. ranges)
- //BUG(sp, "Duplicate terminal - Existing goes to arm " << e << ", new goes to arm " << arm_index );
- )
- )
- branch = Branch::make_Terminal(arm_index);
- });
+ return rv;
+ }
+ void push_arm(const ::std::vector<PatternRule>& x, size_t arm_idx, size_t pat_idx)
+ {
+ assert(is_arm_indexes);
+ rule_sets.push_back(&x);
+ assert(arm_idx <= 0xFFF);
+ assert(pat_idx <= 0xFFF);
+ arm_idxes.push_back(arm_idx | (pat_idx << 12));
}
- // `and_then` - Closure called after processing the final rule
- void populate_tree_from_rule(const Span& sp, const PatternRule* first_rule, unsigned int rule_count, ::std::function<void(Branch&)> and_then);
-
- /// Simplifies the tree by eliminating nodes that don't make a decision
- void simplify();
- /// Propagate the m_default arm's contents to value arms, and vice-versa
- void propagate_default();
- /// HELPER: Unfies the rules from the provided branch with this node
- void unify_from(const Branch& b);
-
- ::MIR::LValue get_field(const ::MIR::LValue& base, unsigned int base_depth) const {
- ::MIR::LValue cur = base.clone();
- for(unsigned int i = base_depth; i < m_field_path.size(); i ++ ) {
- const auto idx = m_field_path.data[i];
- if( idx == FIELD_DEREF ) {
- cur = ::MIR::LValue::make_Deref({ box$(cur) });
+ void push_bb(const ::std::vector<PatternRule>& x, ::MIR::BasicBlockId bb)
+ {
+ assert(!is_arm_indexes);
+ rule_sets.push_back(&x);
+ arm_idxes.push_back(bb);
+ }
+
+ friend ::std::ostream& operator<<(::std::ostream& os, const t_rules_subset& x) {
+ os << "t_rules_subset{";
+ for(size_t i = 0; i < x.rule_sets.size(); i ++)
+ {
+ if(i != 0)
+ os << ", ";
+ os << "[";
+ if(x.is_arm_indexes)
+ {
+ os << (x.arm_idxes[i] & 0xFFF) << "," << (x.arm_idxes[i] >> 12);
}
- else {
- cur = ::MIR::LValue::make_Field({ box$(cur), idx });
+ else
+ {
+ os << "bb" << x.arm_idxes[i];
}
+ os << "]";
+ os << ": " << *x.rule_sets[i];
}
- return cur;
+ os << "}";
+ return os;
}
-
- friend ::std::ostream& operator<<(::std::ostream& os, const Branch& x);
- friend ::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x);
};
-struct DecisionTreeGen
+class MatchGenGrouped
{
+ const Span& sp;
MirBuilder& m_builder;
- const ::std::vector< ::MIR::BasicBlockId>& m_rule_blocks;
+ const ::HIR::TypeRef& m_top_ty;
+ const ::MIR::LValue& m_top_val;
+ const ::std::vector<ArmCode>& m_arms_code;
+
+ size_t m_field_path_ofs;
+public:
+ MatchGenGrouped(MirBuilder& builder, const Span& sp, const ::HIR::TypeRef& top_ty, const ::MIR::LValue& top_val, const ::std::vector<ArmCode>& arms_code, size_t field_path_ofs):
+ sp(sp),
+ m_builder(builder),
+ m_top_ty(top_ty),
+ m_top_val(top_val),
+ m_arms_code(arms_code),
+ m_field_path_ofs(field_path_ofs)
+ {
+ }
- DecisionTreeGen(MirBuilder& builder, const ::std::vector< ::MIR::BasicBlockId >& rule_blocks):
- m_builder( builder ),
- m_rule_blocks( rule_blocks )
- {}
+ void gen_for_slice(t_rules_subset rules, size_t ofs, ::MIR::BasicBlockId default_arm);
+ void gen_dispatch(const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk);
+ void gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk);
+ void gen_dispatch__enum(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk);
+ void gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk);
- ::MIR::BasicBlockId get_block_for_rule(unsigned int rule_index) {
- return m_rule_blocks.at( rule_index );
- }
+ void gen_dispatch_range(const field_path_t& field_path, const ::MIR::Constant& first, const ::MIR::Constant& last, ::MIR::BasicBlockId def_blk);
+ void gen_dispatch_splitslice(const field_path_t& field_path, const PatternRule::Data_SplitSlice& e, ::MIR::BasicBlockId def_blk);
- void generate_tree_code(const Span& sp, const DecisionTreeNode& node, const ::HIR::TypeRef& ty, const ::MIR::LValue& val) {
- generate_tree_code(sp, node, ty, 0, val, [&](const auto& n){
- DEBUG("node = " << n);
- // - Recurse on this method
- this->generate_tree_code(sp, n, ty, val);
- });
+ ::MIR::LValue push_compare(::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right)
+ {
+ return m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool,
+ ::MIR::RValue::make_BinOp({ mv$(left), op, mv$(right) })
+ );
}
- void generate_tree_code(
- const Span& sp,
- const DecisionTreeNode& node,
- const ::HIR::TypeRef& ty, unsigned int path_ofs, const ::MIR::LValue& base_val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
-
- void generate_branch(const DecisionTreeNode::Branch& branch, ::std::function<void(const DecisionTreeNode&)> cb);
-
- // HELPER
- ::MIR::LValue push_compare(const Span& sp, ::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right);
-
- void generate_branches_Signed(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Signed& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Unsigned(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Unsigned& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Float(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Float& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Char(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Unsigned& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Bool(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Bool& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Borrow_str(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_String& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
-
- void generate_branches_Enum(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Variant& branches,
- const field_path_t& field_path, // used to know when to stop handling sub-nodes
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_branches_Slice(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Slice& branches,
- const field_path_t& field_path,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
- void generate_tree_code__enum(
- const Span& sp,
- const DecisionTreeNode& node, const ::HIR::TypeRef& fake_ty, const ::MIR::LValue& val,
- const field_path_t& path_prefix,
- ::std::function<void(const DecisionTreeNode&)> and_then
- );
};
-void MIR_LowerHIR_Match_DecisionTree( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector<ArmCode> arms_code, ::MIR::BasicBlockId first_cmp_block )
+void MIR_LowerHIR_Match_Grouped(
+ MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val,
+ t_arm_rules arm_rules, ::std::vector<ArmCode> arms_code, ::MIR::BasicBlockId first_cmp_block
+ )
{
- TRACE_FUNCTION;
+ TRACE_FUNCTION_F("");
- // XXX XXX XXX: The current codegen (below) will generate incorrect code if ordering matters.
- // ```
- // match ("foo", "bar")
- // {
- // (_, "bar") => {}, // Expected
- // ("foo", _) => {}, // Actual
- // _ => {},
- // }
- // ```
-
- // TODO: Sort the columns in `arm_rules` to ensure that the most specific rule is parsed first.
- // - Ordering within a pattern doesn't matter, only the order of arms matters.
- // - This sort could be designed such that the above case would match correctly?
-
- DEBUG("- Generating rule bindings");
- ::std::vector< ::MIR::BasicBlockId> rule_blocks;
- for(const auto& rule : arm_rules)
+ // - Create a "slice" of the passed rules, suitable for passing to the recursive part of the algo
+ t_rules_subset rules { arm_rules.size(), /*is_arm_indexes=*/true };
+ for(const auto& r : arm_rules)
{
- const auto& arm_code = arms_code[rule.arm_idx];
- ASSERT_BUG(node.span(), !arm_code.has_condition, "Decision tree doesn't (yet) support conditionals");
-
- assert( rule.pat_idx < arm_code.destructures.size() );
- // Set the target for when a rule succeeds to the destructuring code for this rule
- rule_blocks.push_back( arm_code.destructures[rule.pat_idx] );
- // - Tie the end of that block to the code block for this arm
- builder.set_cur_block( rule_blocks.back() );
- builder.end_block( ::MIR::Terminator::make_Goto(arm_code.code) );
+ rules.push_arm( r.m_rules, r.arm_idx, r.pat_idx );
}
+ auto inst = MatchGenGrouped { builder, node.span(), node.m_value->m_res_type, match_val, arms_code, 0 };
- // - Build tree by running each arm's pattern across it
- DEBUG("- Building decision tree");
- DecisionTreeNode root_node({});
- unsigned int rule_idx = 0;
- for( const auto& arm_rule : arm_rules )
- {
- auto arm_idx = arm_rule.arm_idx;
- DEBUG("(" << arm_idx << ", " << arm_rule.pat_idx << "): " << arm_rule.m_rules);
- root_node.populate_tree_from_rule( node.m_arms[arm_idx].m_code->span(), rule_idx, arm_rule.m_rules.data(), arm_rule.m_rules.size() );
- rule_idx += 1;
- }
- DEBUG("root_node = " << root_node);
- root_node.simplify();
- DEBUG("root_node = " << root_node);
- root_node.propagate_default();
- DEBUG("root_node = " << root_node);
- // TODO: Pretty print `root_node`
-
- // - Convert the above decision tree into MIR
- DEBUG("- Emitting decision tree");
- DecisionTreeGen gen { builder, rule_blocks };
- builder.set_cur_block( first_cmp_block );
- gen.generate_tree_code( node.span(), root_node, node.m_value->m_res_type, mv$(match_val) );
- ASSERT_BUG(node.span(), !builder.block_active(), "Decision tree didn't terminate the final block");
-}
+ // NOTE: This block should never be used
+ auto default_arm = builder.new_bb_unlinked();
-#if 0
-DecisionTreeNode MIR_LowerHIR_Match_DecisionTree__MakeTree(const Span& sp, t_arm_rules& arm_rules)
-{
- ::std::vector<unsigned int> indexes;
- ::std::vector< slice<PatternRule> > rules;
- for(unsigned i = 0; i < arm_rules.size(); i ++)
- {
- rules.push_back( arm_rules[i].m_rules );
- indexes.push_back(i);
- }
+ builder.set_cur_block( first_cmp_block );
+ inst.gen_for_slice( mv$(rules), 0, default_arm );
- return MIR_LowerHIR_Match_DecisionTree__MakeTree_Node(sp, indexes, rules);
+ // Make the default infinite loop.
+ // - Preferably, it'd abort.
+ builder.set_cur_block(default_arm);
+ builder.end_block( ::MIR::Terminator::make_Goto(default_arm) );
}
-DecisionTreeNode MIR_LowerHIR_Match_DecisionTree__MakeTree_Node(const Span& sp, slice<unsigned int> arm_indexes, slice< slice<PaternRule>> arm_rules)
+void MatchGenGrouped::gen_for_slice(t_rules_subset arm_rules, size_t ofs, ::MIR::BasicBlockId default_arm)
{
- assert( arm_indexes.size() == arm_rules.size() );
- assert( arm_rules.size() > 1 );
- assert( arm_rules[0].size() > 0 );
+ TRACE_FUNCTION_F("arm_rules=" << arm_rules << ", ofs="<<ofs);
+ ASSERT_BUG(sp, arm_rules.size() > 0, "");
- // 1. Sort list (should it already be sorted?)
- for(const auto& rules : arm_rules)
+ // Quick hack: Skip any layers entirely made up of PatternRule::Any
+ for(;;)
{
- ASSERT_BUG(sp, rules.size() != arm_rules[0].size(), "");
- }
-
- // 2. Detect all arms being `_` and move on to the next condition
- while( ::std::all_of(arm_rules.begin(), arm_rules.end(), [](const auto& r){ return r.m_rules[0].is_Any(); }) )
- {
- // Delete first rule from all and continue.
- if( arm_rules[0].size() == 1 ) {
- // No rules left?
- BUG(sp, "Duplicate match arms");
+ bool is_all_any = true;
+ for(size_t i = 0; i < arm_rules.size() && is_all_any; i ++)
+ {
+ if( arm_rules[i].size() <= ofs )
+ is_all_any = false;
+ else if( ! arm_rules[i][ofs].is_Any() )
+ is_all_any = false;
}
-
- for(auto& rules : arm_rules)
+ if( ! is_all_any )
{
- rules = rules.subslice_from(1);
+ break ;
}
+ ofs ++;
+ DEBUG("Skip to ofs=" << ofs);
}
- // We have a codition.
- for(const auto& rules : arm_rules)
+ // Split current set of rules into groups based on _ patterns
+ for(size_t idx = 0; idx < arm_rules.size(); )
{
- ASSERT_BUG(sp, rules[0].is_Any() || rules[0].tag() == arm_rules[0][0].tag(), "Mismatched rules in match");
- }
-
- bool has_any = arm_rules.back()[0].is_Any();
+ // Completed arms
+ while( idx < arm_rules.size() && arm_rules[idx].size() <= ofs )
+ {
+ auto next = idx+1 == arm_rules.size() ? default_arm : m_builder.new_bb_unlinked();
+ ASSERT_BUG(sp, arm_rules[idx].size() == ofs, "Offset too large for rule - ofs=" << ofs << ", rules=" << arm_rules[idx]);
+ DEBUG(idx << ": Complete");
+ // Emit jump to either arm code, or arm condition
+ if( arm_rules.is_arm() )
+ {
+ auto ai = arm_rules.arm_idx(idx);
+ ASSERT_BUG(sp, m_arms_code.size() > 0, "Bottom-level ruleset with no arm code information");
+ const auto& ac = m_arms_code[ai.first];
- // All rules must either be _ or the same type, and can't all be _
- switch( arm_rules[0][0].tag() )
- {
- case PatternRule::TAGDEAD: throw "";
- case PatternRule::TAG_Any: throw "";
+ m_builder.end_block( ::MIR::Terminator::make_Goto(ac.destructures[ai.second]) );
+ m_builder.set_cur_block( ac.destructures[ai.second] );
- case PatternRule::TAG_Variant:
- break;
- // TODO: Value and ValueRange can appear together.
- // - They also overlap in non-trivial ways.
- }
-}
-#endif
-
-// ----------------------------
-// DecisionTreeNode
-// ----------------------------
-DecisionTreeNode::Branch DecisionTreeNode::clone(const DecisionTreeNode::Branch& b) {
- TU_MATCHA( (b), (e),
- (Unset, return Branch(e); ),
- (Subtree, return Branch(box$( e->clone() )); ),
- (Terminal, return Branch(e); )
- )
- throw "";
-}
-DecisionTreeNode::Values DecisionTreeNode::clone(const DecisionTreeNode::Values& x) {
- TU_MATCHA( (x), (e),
- (Unset, return Values(e); ),
- (Bool,
- return Values::make_Bool({ clone(e.false_branch), clone(e.true_branch) });
- ),
- (Variant,
- Values::Data_Variant rv;
- rv.reserve(e.size());
- for(const auto& v : e)
- rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- ),
- (Unsigned,
- Values::Data_Unsigned rv;
- rv.reserve(e.size());
- for(const auto& v : e)
- rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- ),
- (Signed,
- Values::Data_Signed rv;
- rv.reserve(e.size());
- for(const auto& v : e)
- rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- ),
- (Float,
- Values::Data_Float rv;
- rv.reserve(e.size());
- for(const auto& v : e)
- rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- ),
- (String,
- Values::Data_String rv;
- rv.reserve(e.size());
- for(const auto& v : e)
- rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- ),
- (Slice,
- Values::Data_Slice rv;
- rv.fixed_arms.reserve(e.fixed_arms.size());
- for(const auto& v : e.fixed_arms)
- rv.fixed_arms.push_back( ::std::make_pair(v.first, clone(v.second)) );
- return Values( mv$(rv) );
- )
- )
- throw "";
-}
-DecisionTreeNode DecisionTreeNode::clone() const {
- DecisionTreeNode rv(m_field_path);
- rv.m_field_path = m_field_path;
- rv.m_branches = clone(m_branches);
- rv.m_default = clone(m_default);
- return rv;
-}
+ if( ac.has_condition )
+ {
+ TODO(sp, "Handle conditionals in Grouped");
+ // TODO: If the condition fails, this should re-try the match on other rules that could have worked.
+ // - For now, conditionals are disabled.
-// Helpers for `populate_tree_from_rule`
-namespace
-{
- DecisionTreeNode::Branch new_branch_subtree(field_path_t path)
- {
- return DecisionTreeNode::Branch( box$(DecisionTreeNode( mv$(path) )) );
- }
+ // TODO: What if there's multiple patterns on this condition?
+ // - For now, only the first pattern gets edited.
+ // - Maybe clone the blocks used for the condition?
+ m_builder.end_block( ::MIR::Terminator::make_Goto(ac.cond_start) );
- // Common code for numerics (Int, Uint, and Float)
- template<typename T>
- static void from_rule_value(
- const Span& sp,
- ::std::vector< ::std::pair< DecisionTreeNode::Range<T>, DecisionTreeNode::Branch> >& be, T ve,
- const char* name, const field_path_t& field_path, ::std::function<void(DecisionTreeNode::Branch&)> and_then
- )
- {
- auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve; });
- if( it == be.end() || it->first.start > ve ) {
- it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve,ve }, new_branch_subtree(field_path) ) );
- }
- else if( it->first.start == ve && it->first.end == ve ) {
- // Equal, continue and add sub-pat
- }
- else {
- // Collide or overlap!
- TODO(sp, "Value patterns - " << name << " - Overlapping - " << it->first.start << " <= " << ve << " <= " << it->first.end);
- }
- and_then( it->second );
- }
+ // Check for marking in `ac` that the block has already been terminated, assert that target is `next`
+ if( ai.second == 0 )
+ {
+ if( ac.cond_fail_tgt != 0)
+ {
+ ASSERT_BUG(sp, ac.cond_fail_tgt == next, "Condition fail target already set with mismatching arm, set to bb" << ac.cond_fail_tgt << " cur is bb" << next);
+ }
+ else
+ {
+ ac.cond_fail_tgt = next;
- template<typename T>
- static void from_rule_valuerange(
- const Span& sp,
- ::std::vector< ::std::pair< DecisionTreeNode::Range<T>, DecisionTreeNode::Branch> >& be, T ve_start, T ve_end,
- const char* name, const field_path_t& field_path, ::std::function<void(DecisionTreeNode::Branch&)> and_then
- )
- {
- TRACE_FUNCTION_F("be=[" << FMT_CB(os, for(const auto& i:be) os << i.first <<" , ";) << "], new=" << ve_start << "..." << ve_end);
- ASSERT_BUG(sp, ve_start <= ve_end, "Range pattern with a start after the end - " << ve_start << "..." << ve_end);
+ m_builder.set_cur_block( ac.cond_end );
+ m_builder.end_block( ::MIR::Terminator::make_If({ ac.cond_lval.clone(), ac.code, next }) );
+ }
+ }
- if( ve_start == ve_end ) {
- from_rule_value(sp, be, ve_start, name, field_path, and_then);
- return ;
- }
- // - Find the first entry that ends after the new one starts.
- auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve_start; });
- while(ve_start < ve_end)
- {
- if( it == be.end() ) {
- DEBUG("new = (" << ve_start << "..." << ve_end << "), exist=END");
- it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start,ve_end }, new_branch_subtree(field_path) ) );
- and_then(it->second);
- return ;
- }
- DEBUG("new = (" << ve_start << "..." << ve_end << "), exist=" << it->first);
- // If the located entry starts after the end of this range
- if( it->first.start >= ve_end ) {
- DEBUG("- New free");
- it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start,ve_end }, new_branch_subtree(field_path) ) );
- and_then(it->second);
- return ;
- }
- // If this range is equal to the existing, just recurse into it
- else if( it->first.start == ve_start && it->first.end == ve_end ) {
- DEBUG("- Equal");
- and_then(it->second);
- return ;
- }
- // If the new range starts before the start of this range, add a new entry before the existing one
- else if( it->first.start > ve_start ) {
- DEBUG("- New head, continue");
- it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start,it->first.start-1 }, new_branch_subtree(field_path) ) );
- and_then(it->second);
- ++ it;
- ve_start = it->first.start;
- }
- // If the new range ends before the end of this range, split the existing range and recurse into the first
- else if( it->first.end > ve_end ) {
- DEBUG("- Inner");
- assert(ve_start == it->first.start);
- it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start, ve_end }, DecisionTreeNode::clone(it->second) ) );
- and_then(it->second);
- (it+1)->first.start = ve_end+1;
- return ;
+ if( next != default_arm )
+ m_builder.set_cur_block(next);
+ }
+ else
+ {
+ m_builder.end_block( ::MIR::Terminator::make_Goto(ac.code) );
+ ASSERT_BUG(sp, idx+1 == arm_rules.size(), "Ended arm with other arms present");
+ }
}
- // (else) if the new range ends after the end of this range, apply to the rest of this range and advance
- else {
- DEBUG("- Shared head, continue");
- //assert(it->first.start == ve_start);
- assert((it->first.end) < ve_end);
-
- and_then(it->second);
- ve_start = it->first.end + 1;
- ++ it;
+ else
+ {
+ auto bb = arm_rules.bb_idx(idx);
+ m_builder.end_block( ::MIR::Terminator::make_Goto(bb) );
+ while( idx+1 < arm_rules.size() && bb == arm_rules.bb_idx(idx) && arm_rules[idx].size() == ofs )
+ idx ++;
+ ASSERT_BUG(sp, idx+1 == arm_rules.size(), "Ended arm (inner) with other arms present");
}
+ idx ++;
}
- }
-}
-void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule* first_rule, unsigned int rule_count, ::std::function<void(Branch&)> and_then)
-{
- assert( rule_count > 0 );
- const auto& rule = *first_rule;
- if( m_field_path.size() == 0 ) {
- m_field_path = rule.field_path;
- }
- else {
- ASSERT_BUG(sp, m_field_path == rule.field_path, "Patterns with mismatched field paths - " << m_field_path << " != " << rule.field_path);
- }
-
- #define GET_BRANCHES(fld, var) (({if( fld.is_Unset() ) {\
- fld = Values::make_##var({}); \
- } \
- else if( !fld.is_##var() ) { \
- BUG(sp, "Mismatched rules - have " #var ", but have seen " << fld.tag_str()); \
- }}), \
- fld.as_##var())
-
-
- TU_MATCHA( (rule), (e),
- (Any, {
- if( rule_count == 1 )
+ // - Value arms
+ auto start = idx;
+ for(; idx < arm_rules.size() ; idx ++)
{
- ASSERT_BUG(sp, !m_default.is_Terminal(), "Duplicate terminal rule");
- and_then(m_default);
- }
- else
- {
- if( m_default.is_Unset() ) {
- m_default = new_branch_subtree(rule.field_path);
- m_default.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else TU_IFLET( Branch, m_default, Subtree, be,
- be->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- )
- else {
- // NOTE: All lists processed as part of the same tree should be the same length
- BUG(sp, "Duplicate terminal rule");
- }
+ if( arm_rules[idx].size() <= ofs )
+ break;
+ if( arm_rules[idx][ofs].is_Any() )
+ break;
+ if( arm_rules[idx][ofs].is_SplitSlice() )
+ break;
+ // TODO: It would be nice if ValueRange could be combined with Value (if there's no overlap)
+ if( arm_rules[idx][ofs].is_ValueRange() )
+ break;
}
- // TODO: Should this also recurse into branches?
- }),
- (Variant, {
- auto& be = GET_BRANCHES(m_branches, Variant);
+ auto first_any = idx;
- auto it = ::std::find_if( be.begin(), be.end(), [&](const auto& x){ return x.first >= e.idx; });
- // If this variant isn't yet processed, add a new subtree for it
- if( it == be.end() || it->first != e.idx ) {
- it = be.insert(it, ::std::make_pair(e.idx, new_branch_subtree(rule.field_path)));
- assert( it->second.is_Subtree() );
- }
- else {
- if( it->second.is_Terminal() ) {
- BUG(sp, "Duplicate terminal rule - " << it->second.as_Terminal());
- }
- assert( !it->second.is_Unset() );
- assert( it->second.is_Subtree() );
- }
- auto& subtree = *it->second.as_Subtree();
+ // Generate dispatch based on the above list
+ // - If there's value ranges they need special handling
+ // - Can sort arms within this group (ordering doesn't matter, as long as ranges are handled)
+ // - Sort must be stable.
- if( e.sub_rules.size() > 0 && rule_count > 1 )
+ if( start < first_any )
{
- subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), [&](auto& branch){
- TU_MATCH_DEF(Branch, (branch), (be),
- (
- BUG(sp, "Duplicate terminator");
- ),
- (Unset,
- branch = new_branch_subtree(rule.field_path);
- ),
- (Subtree,
- )
- )
- branch.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- });
- }
- else if( e.sub_rules.size() > 0)
- {
- subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), and_then);
- }
- else if( rule_count > 1 )
- {
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(it->second);
- }
- }),
- (Slice,
- auto& be = GET_BRANCHES(m_branches, Slice);
-
- auto it = ::std::find_if( be.fixed_arms.begin(), be.fixed_arms.end(), [&](const auto& x){ return x.first >= e.len; } );
- if( it == be.fixed_arms.end() || it->first != e.len ) {
- it = be.fixed_arms.insert(it, ::std::make_pair(e.len, new_branch_subtree(rule.field_path)));
- }
- else {
- if( it->second.is_Terminal() ) {
- BUG(sp, "Duplicate terminal rule - " << it->second.as_Terminal());
+ DEBUG(start << "+" << (first_any-start) << ": Values");
+ bool has_default = (first_any < arm_rules.size());
+ auto next = (has_default ? m_builder.new_bb_unlinked() : default_arm);
+
+ // Sort rules before getting compatible runs
+ // TODO: Is this a valid operation?
+ arm_rules.sub_sort(ofs, start, first_any - start);
+
+ // Create list of compatible arm slices (runs with the same selector value)
+ ::std::vector<t_rules_subset> slices;
+ auto cur_test = start;
+ for(auto i = start; i < first_any; i ++)
+ {
+ // Just check if the decision value differs (don't check nested rules)
+ if( ! rule_compatible(arm_rules[i][ofs], arm_rules[cur_test][ofs]) )
+ {
+ slices.push_back( arm_rules.sub_slice(cur_test, i - cur_test) );
+ cur_test = i;
+ }
}
- assert( !it->second.is_Unset() );
- }
- assert( it->second.is_Subtree() );
- auto& subtree = *it->second.as_Subtree();
+ slices.push_back( arm_rules.sub_slice(cur_test, first_any - cur_test) );
+ DEBUG("- " << slices.size() << " groupings");
+ ::std::vector<::MIR::BasicBlockId> arm_blocks;
+ arm_blocks.reserve( slices.size() );
- if( e.sub_rules.size() > 0 && rule_count > 1 )
- {
- subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), [&](auto& branch){
- TU_MATCH_DEF(Branch, (branch), (be),
- (
- BUG(sp, "Duplicate terminator");
- ),
- (Unset,
- branch = new_branch_subtree(rule.field_path);
- ),
- (Subtree,
- )
- )
- branch.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- });
- }
- else if( e.sub_rules.size() > 0)
- {
- subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), and_then);
- }
- else if( rule_count > 1 )
- {
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(it->second);
- }
- ),
- (SplitSlice,
- //auto& be = GET_BRANCHES(m_branches, Slice);
- TODO(sp, "SplitSlice in DTN - " << rule);
- ),
- (Bool,
- auto& be = GET_BRANCHES(m_branches, Bool);
+ auto cur_blk = m_builder.pause_cur_block();
+ // > Stable sort list
+ ::std::sort( slices.begin(), slices.end(), [&](const auto& a, const auto& b){ return a[0][ofs] < b[0][ofs]; } );
+ // TODO: Should this do a stable sort of inner patterns too?
+ // - A sort of inner patterns such that `_` (and range?) patterns don't change position.
- auto& branch = (e ? be.true_branch : be.false_branch);
- if( branch.is_Unset() ) {
- branch = new_branch_subtree( rule.field_path );
- }
- else if( branch.is_Terminal() ) {
- BUG(sp, "Duplicate terminal rule - " << branch.as_Terminal());
- }
- else {
- // Good.
- }
- if( rule_count > 1 )
- {
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
- ),
- (Value,
- TU_MATCHA( (e), (ve),
- (Int,
- auto& be = GET_BRANCHES(m_branches, Signed);
-
- from_rule_value(sp, be, ve.v, "Signed", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 ) {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
- });
- ),
- (Uint,
- auto& be = GET_BRANCHES(m_branches, Unsigned);
-
- from_rule_value(sp, be, ve.v, "Unsigned", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 ) {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
+ // > Get type of match, generate dispatch list.
+ for(size_t i = 0; i < slices.size(); i ++)
+ {
+ // If rules are actually different, split here. (handles Enum and Slice)
+ auto cur_block = m_builder.new_bb_unlinked();
+ m_builder.set_cur_block(cur_block);
+ auto cur_start = 0;
+ for(size_t j = 0; j < slices[i].size(); j ++)
+ {
+ if(slices[i][j][ofs] != slices[i][cur_start][ofs])
{
- and_then(branch);
- }
- });
- ),
- (Float,
- auto& be = GET_BRANCHES(m_branches, Float);
-
- from_rule_value(sp, be, ve.v, "Float", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 ) {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else {
- and_then(branch);
- }
- });
- ),
- (Bool,
- BUG(sp, "Hit Bool in PatternRule::Value - " << e);
- ),
- (Bytes,
- TODO(sp, "Value patterns - Bytes");
- ),
- (StaticString,
- auto& be = GET_BRANCHES(m_branches, String);
+ DEBUG("- Equal range : " << cur_start << "+" << (j - cur_start));
+ // Package up previous rules and generate dispatch code
+ auto ns = slices[i].sub_slice(cur_start, j - cur_start);
+ this->gen_for_slice(mv$(ns), ofs+1, next);
- auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first >= ve; });
- if( it == be.end() || it->first != ve ) {
- it = be.insert( it, ::std::make_pair(ve, new_branch_subtree(rule.field_path) ) );
- }
- auto& branch = it->second;
- if( rule_count > 1 )
- {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
- ),
- (Const,
- BUG(sp, "Hit Const in PatternRule::Value - " << e);
- ),
- (ItemAddr,
- BUG(sp, "Hit ItemAddr in PatternRule::Value - " << e);
- )
- )
- ),
- (ValueRange,
+ cur_block = m_builder.new_bb_unlinked();
+ m_builder.set_cur_block(cur_block);
- ASSERT_BUG(sp, e.first.tag() == e.last.tag(), "Constant type mismatch in ValueRange - " << e.first << " and " << e.last);
- TU_MATCHA( (e.first, e.last), (ve_start, ve_end),
- (Int,
- auto& be = GET_BRANCHES(m_branches, Signed);
- from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Signed", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 )
- {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
+ cur_start = j;
}
- });
- ),
- (Uint,
- // TODO: Share code between the three numeric groups
- auto& be = GET_BRANCHES(m_branches, Unsigned);
- from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Unsigned", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 )
- {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
- });
- ),
- (Float,
- auto& be = GET_BRANCHES(m_branches, Float);
- from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Float", rule.field_path,
- [&](auto& branch) {
- if( rule_count > 1 )
- {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
- });
- ),
- (Bool,
- BUG(sp, "Hit Bool in PatternRule::ValueRange - " << e.first);
- ),
- (Bytes,
- TODO(sp, "ValueRange patterns - Bytes");
- ),
- (StaticString,
- ERROR(sp, E0000, "Use of string in value range patter");
- ),
- (Const,
- BUG(sp, "Hit Const in PatternRule::ValueRange - " << e.first);
- ),
- (ItemAddr,
- BUG(sp, "Hit ItemAddr in PatternRule::ValueRange - " << e.first);
- )
- )
- )
- )
-}
-
-void DecisionTreeNode::simplify()
-{
- struct H {
- static void simplify_branch(Branch& b)
- {
- TU_IFLET(Branch, b, Subtree, be,
- be->simplify();
- if( be->m_branches.is_Unset() ) {
- auto v = mv$( be->m_default );
- b = mv$(v);
+ arm_blocks.push_back(cur_block);
}
- )
- }
- };
-
- TU_MATCHA( (m_branches), (e),
- (Unset,
- H::simplify_branch(m_default);
- // Replace `this` with `m_default` if `m_default` is a subtree
- // - Fixes the edge case for the top of the tree
- if( m_default.is_Subtree() )
- {
- *this = mv$(*m_default.as_Subtree());
- }
- return ;
- ),
- (Bool,
- H::simplify_branch(e.false_branch);
- H::simplify_branch(e.true_branch);
- ),
- (Variant,
- for(auto& branch : e) {
- H::simplify_branch(branch.second);
- }
- ),
- (Unsigned,
- for(auto& branch : e) {
- H::simplify_branch(branch.second);
- }
- ),
- (Signed,
- for(auto& branch : e) {
- H::simplify_branch(branch.second);
- }
- ),
- (Float,
- for(auto& branch : e) {
- H::simplify_branch(branch.second);
- }
- ),
- (String,
- for(auto& branch : e) {
- H::simplify_branch(branch.second);
- }
- ),
- (Slice,
- for(auto& branch : e.fixed_arms) {
- H::simplify_branch(branch.second);
- }
- )
- )
- H::simplify_branch(m_default);
-}
-
-void DecisionTreeNode::propagate_default()
-{
- TRACE_FUNCTION_FR(*this, *this);
- struct H {
- static void handle_branch(Branch& b, const Branch& def) {
- TU_IFLET(Branch, b, Subtree, be,
- be->propagate_default();
- if( !def.is_Unset() )
+ if( cur_start != 0 )
{
- DEBUG("Unify " << *be << " with " << def);
- be->unify_from(def);
- be->propagate_default();
+ DEBUG("- Equal range : " << cur_start << "+" << (slices[i].size() - cur_start));
+ auto ns = slices[i].sub_slice(cur_start, slices[i].size() - cur_start);
+ this->gen_for_slice( mv$(ns), ofs+1, next);
}
- )
- }
- };
-
- TU_MATCHA( (m_branches), (e),
- (Unset,
- ),
- (Bool,
- DEBUG("- false");
- H::handle_branch(e.false_branch, m_default);
- DEBUG("- true");
- H::handle_branch(e.true_branch, m_default);
- ),
- (Variant,
- for(auto& branch : e) {
- DEBUG("- V " << branch.first);
- H::handle_branch(branch.second, m_default);
- }
- ),
- (Unsigned,
- for(auto& branch : e) {
- DEBUG("- U " << branch.first);
- H::handle_branch(branch.second, m_default);
- }
- ),
- (Signed,
- for(auto& branch : e) {
- DEBUG("- S " << branch.first);
- H::handle_branch(branch.second, m_default);
- }
- ),
- (Float,
- for(auto& branch : e) {
- DEBUG("- " << branch.first);
- H::handle_branch(branch.second, m_default);
- }
- ),
- (String,
- for(auto& branch : e) {
- DEBUG("- '" << branch.first << "'");
- H::handle_branch(branch.second, m_default);
- }
- ),
- (Slice,
- for(auto& branch : e.fixed_arms) {
- DEBUG("- [_;" << branch.first << "]");
- H::handle_branch(branch.second, m_default);
- }
- )
- )
- DEBUG("- default");
- TU_IFLET(Branch, m_default, Subtree, be,
- be->propagate_default();
-
- if( be->m_default.is_Unset() ) {
- // Propagate default from value branches
- TU_MATCHA( (m_branches), (e),
- (Unset,
- ),
- (Bool,
- be->unify_from(e.false_branch);
- be->unify_from(e.true_branch);
- ),
- (Variant,
- for(auto& branch : e) {
- be->unify_from(branch.second);
- }
- ),
- (Unsigned,
- for(auto& branch : e) {
- be->unify_from(branch.second);
- }
- ),
- (Signed,
- for(auto& branch : e) {
- be->unify_from(branch.second);
- }
- ),
- (Float,
- for(auto& branch : e) {
- be->unify_from(branch.second);
- }
- ),
- (String,
- for(auto& branch : e) {
- be->unify_from(branch.second);
- }
- ),
- (Slice,
- for(auto& branch : e.fixed_arms) {
- be->unify_from(branch.second);
+ else
+ {
+ this->gen_for_slice(slices[i], ofs+1, next);
}
- )
- )
- }
- )
-}
-
-namespace {
- static void unify_branch(DecisionTreeNode::Branch& dst, const DecisionTreeNode::Branch& src) {
- if( dst.is_Unset() ) {
- dst = DecisionTreeNode::clone(src);
- }
- else if( dst.is_Subtree() ) {
- dst.as_Subtree()->unify_from(src);
- }
- else {
- // Terminal, no unify
- }
- }
-
- template<typename T>
- void unify_from_vals_range(::std::vector< ::std::pair<T, DecisionTreeNode::Branch>>& dst, const ::std::vector< ::std::pair<T, DecisionTreeNode::Branch>>& src)
- {
- for(const auto& srcv : src)
- {
- // Find the first entry with an end greater than or equal to the start of this entry
- auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first.end >= srcv.first.start; });
- // Not found? Insert a new branch
- if( it == dst.end() ) {
- it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second)));
}
- // If the found entry doesn't overlap (the start of `*it` is after the end of `srcv`)
- else if( it->first.start > srcv.first.end ) {
- it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second)));
- }
- else if( it->first == srcv.first ) {
- unify_branch( it->second, srcv.second );
- }
- else {
- // NOTE: Overlapping doesn't get handled here
- }
- }
- }
- template<typename T>
- void unify_from_vals_pt(::std::vector< ::std::pair<T, DecisionTreeNode::Branch>>& dst, const ::std::vector< ::std::pair<T, DecisionTreeNode::Branch>>& src)
- {
- // Insert items not already present, merge present items
- for(const auto& srcv : src)
- {
- auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first >= srcv.first; });
- // Not found? Insert a new branch
- if( it == dst.end() || it->first != srcv.first ) {
- it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second)));
- }
- else {
- unify_branch( it->second, srcv.second );
- }
- }
- }
-}
+ m_builder.set_cur_block(cur_blk);
-void DecisionTreeNode::unify_from(const Branch& b)
-{
- TRACE_FUNCTION_FR(*this << " with " << b, *this);
+ // Generate decision code
+ this->gen_dispatch(slices, ofs, arm_blocks, next);
- assert( b.is_Terminal() || b.is_Subtree() );
-
- if( m_default.is_Unset() ) {
- if( b.is_Terminal() ) {
- m_default = clone(b);
- }
- else {
- m_default = clone(b.as_Subtree()->m_default);
+ if(has_default)
+ {
+ m_builder.set_cur_block(next);
+ }
}
- }
- if( b.is_Subtree() && b.as_Subtree()->m_branches.tag() != m_branches.tag() ) {
- // Is this a bug, or expected (and don't unify in?)
- DEBUG("TODO - Unify mismatched arms? - " << b.as_Subtree()->m_branches.tag_str() << " and " << m_branches.tag_str());
- return ;
- }
- bool should_unify_subtree = b.is_Subtree() && this->m_field_path == b.as_Subtree()->m_field_path;
- //if( b.is_Subtree() ) {
- // ASSERT_BUG(Span(), this->m_field_path == b.as_Subtree()->m_field_path, "Unifiying DTNs with mismatched paths - " << this->m_field_path << " != " << b.as_Subtree()->m_field_path);
- //}
+ // Collate matching blocks at `first_any`
+ assert(first_any == idx);
+ if( first_any < arm_rules.size() && arm_rules[idx].size() > ofs )
+ {
+ // Collate all equal rules
+ while(idx < arm_rules.size() && arm_rules[idx][ofs] == arm_rules[first_any][ofs])
+ idx ++;
+ DEBUG(first_any << "-" << idx << ": Multi-match");
- TU_MATCHA( (m_branches), (dst),
- (Unset,
- if( b.is_Subtree() ) {
- assert( b.as_Subtree()->m_branches.is_Unset() );
- }
- else {
- // Huh? Terminal matching against an unset branch?
- }
- ),
- (Bool,
- auto* src = (b.is_Subtree() ? &b.as_Subtree()->m_branches.as_Bool() : nullptr);
+ bool has_next = idx < arm_rules.size();
+ auto next = (has_next ? m_builder.new_bb_unlinked() : default_arm);
- unify_branch( dst.false_branch, (src ? src->false_branch : b) );
- unify_branch( dst.true_branch , (src ? src->true_branch : b) );
- ),
- (Variant,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_Variant(), "Unifying Variant with " << sb.tag_str());
- unify_from_vals_pt(dst, sb.as_Variant());
- }
- else {
- // Unify all with terminal branch
- for(auto& dstv : dst)
+ const auto& rule = arm_rules[first_any][ofs];
+ if(const auto* e = rule.opt_ValueRange())
{
- unify_branch(dstv.second, b);
+ // Generate branch based on range
+ this->gen_dispatch_range(arm_rules[first_any][ofs].field_path, e->first, e->last, next);
}
- }
- ),
- (Unsigned,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_Unsigned(), "Unifying Unsigned with " << sb.tag_str());
- unify_from_vals_range(dst, sb.as_Unsigned());
- }
- else {
- for(auto& dstv : dst)
+ else if(const auto* e = rule.opt_SplitSlice())
{
- unify_branch(dstv.second, b);
+ // Generate branch based on slice length being at least required.
+ this->gen_dispatch_splitslice(rule.field_path, *e, next);
}
- }
- ),
- (Signed,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_Signed(), "Unifying Signed with " << sb.tag_str());
- unify_from_vals_range(dst, sb.as_Signed());
- }
- else {
- for(auto& dstv : dst)
+ else
{
- unify_branch(dstv.second, b);
- }
- }
- ),
- (Float,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_Float(), "Unifying Float with " << sb.tag_str());
- unify_from_vals_range(dst, sb.as_Float());
- }
- else {
- for(auto& dstv : dst) {
- unify_branch(dstv.second, b);
- }
- }
- ),
- (String,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_String(), "Unifying String with " << sb.tag_str());
- unify_from_vals_pt(dst, sb.as_String());
- }
- else {
- for(auto& dstv : dst) {
- unify_branch( dstv.second, b );
+ ASSERT_BUG(sp, rule.is_Any(), "Didn't expect non-Any rule here, got " << rule.tag_str() << " " << rule);
}
- }
- ),
- (Slice,
- if( should_unify_subtree ) {
- auto& sb = b.as_Subtree()->m_branches;
- ASSERT_BUG(Span(), sb.is_Slice(), "Unifying Slice with " << sb.tag_str());
- const auto& src = sb.as_Slice();
- unify_from_vals_pt(dst.fixed_arms, src.fixed_arms);
- }
- else {
- for(auto& dstv : dst.fixed_arms) {
- unify_branch( dstv.second, b );
- }
- }
- )
- )
-}
+ // Step deeper into these arms
+ auto slice = arm_rules.sub_slice(first_any, idx - first_any);
+ this->gen_for_slice(mv$(slice), ofs+1, next);
-::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode::Branch& x) {
- TU_MATCHA( (x), (be),
- (Unset,
- os << "!";
- ),
- (Terminal,
- os << "ARM " << be;
- ),
- (Subtree,
- os << *be;
- )
- )
- return os;
-}
-::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x) {
- os << "DTN [" << x.m_field_path << "] { ";
- TU_MATCHA( (x.m_branches), (e),
- (Unset,
- os << "!, ";
- ),
- (Bool,
- os << "false = " << e.false_branch << ", true = " << e.true_branch << ", ";
- ),
- (Variant,
- os << "V ";
- for(const auto& branch : e) {
- os << branch.first << " = " << branch.second << ", ";
- }
- ),
- (Unsigned,
- os << "U ";
- for(const auto& branch : e) {
- const auto& range = branch.first;
- if( range.start == range.end ) {
- os << range.start;
- }
- else {
- os << range.start << "..." << range.end;
- }
- os << " = " << branch.second << ", ";
- }
- ),
- (Signed,
- os << "S ";
- for(const auto& branch : e) {
- const auto& range = branch.first;
- if( range.start == range.end ) {
- os << range.start;
- }
- else {
- os << range.start << "..." << range.end;
- }
- os << " = " << branch.second << ", ";
- }
- ),
- (Float,
- os << "F ";
- for(const auto& branch : e) {
- const auto& range = branch.first;
- if( range.start == range.end ) {
- os << range.start;
- }
- else {
- os << range.start << "..." << range.end;
+ if(has_next)
+ {
+ m_builder.set_cur_block(next);
}
- os << " = " << branch.second << ", ";
- }
- ),
- (String,
- for(const auto& branch : e) {
- os << "\"" << branch.first << "\"" << " = " << branch.second << ", ";
- }
- ),
- (Slice,
- os << "len ";
- for(const auto& branch : e.fixed_arms) {
- os << "=" << branch.first << " = " << branch.second << ", ";
}
- )
- )
+ }
- os << "* = " << x.m_default;
- os << " }";
- return os;
+ ASSERT_BUG(sp, ! m_builder.block_active(), "Block left active after match group");
}
-
-// ----------------------------
-// DecisionTreeGen
-// ----------------------------
-
-void DecisionTreeGen::generate_tree_code(
- const Span& sp,
- const DecisionTreeNode& node,
- const ::HIR::TypeRef& top_ty, unsigned int field_path_ofs, const ::MIR::LValue& top_val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
+void MatchGenGrouped::gen_dispatch(const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk)
{
- TRACE_FUNCTION_F("top_ty=" << top_ty << ", field_path_ofs=" << field_path_ofs << ", top_val=" << top_val << ", node=" << node);
+ const auto& field_path = rules[0][0][ofs].field_path;
+ TRACE_FUNCTION_F("rules=["<<rules <<"], ofs=" << ofs <<", field_path=" << field_path);
+
+ {
+ size_t n = 0;
+ for(size_t i = 0; i < rules.size(); i++)
+ {
+ for(size_t j = 0; j < rules[i].size(); j++)
+ {
+ ASSERT_BUG(sp, rules[i][j][ofs].field_path == field_path, "Field path mismatch, " << rules[i][j][ofs].field_path << " != " << field_path);
+ n ++;
+ }
+ }
+ ASSERT_BUG(sp, arm_targets.size() == n, "Arm target count mismatch - " << n << " != " << arm_targets.size());
+ }
::MIR::LValue val;
::HIR::TypeRef ty;
-
- get_ty_and_val(sp, m_builder.resolve(), top_ty, top_val, node.m_field_path, field_path_ofs, ty, val);
+ get_ty_and_val(sp, m_builder.resolve(), m_top_ty, m_top_val, field_path, m_field_path_ofs, ty, val);
DEBUG("ty = " << ty << ", val = " << val);
- TU_MATCHA( (ty.m_data), (e),
- (Infer, BUG(sp, "Ivar for in match type"); ),
- (Diverge, BUG(sp, "Diverge in match type"); ),
- (Primitive,
- switch(e)
- {
- case ::HIR::CoreType::Bool:
- ASSERT_BUG(sp, node.m_branches.is_Bool(), "Tree for bool isn't a _Bool - node="<<node);
- this->generate_branches_Bool(sp, node.m_default, node.m_branches.as_Bool(), ty, mv$(val), mv$(and_then));
- break;
- case ::HIR::CoreType::U8:
- case ::HIR::CoreType::U16:
- case ::HIR::CoreType::U32:
- case ::HIR::CoreType::U64:
- case ::HIR::CoreType::U128:
- case ::HIR::CoreType::Usize:
- ASSERT_BUG(sp, node.m_branches.is_Unsigned(), "Tree for unsigned isn't a _Unsigned - node="<<node);
- this->generate_branches_Unsigned(sp, node.m_default, node.m_branches.as_Unsigned(), ty, mv$(val), mv$(and_then));
- break;
- case ::HIR::CoreType::I8:
- case ::HIR::CoreType::I16:
- case ::HIR::CoreType::I32:
- case ::HIR::CoreType::I64:
- case ::HIR::CoreType::I128:
- case ::HIR::CoreType::Isize:
- ASSERT_BUG(sp, node.m_branches.is_Signed(), "Tree for unsigned isn't a _Signed - node="<<node);
- this->generate_branches_Signed(sp, node.m_default, node.m_branches.as_Signed(), ty, mv$(val), mv$(and_then));
- break;
- case ::HIR::CoreType::Char:
- ASSERT_BUG(sp, node.m_branches.is_Unsigned(), "Tree for char isn't a _Unsigned - node="<<node);
- this->generate_branches_Char(sp, node.m_default, node.m_branches.as_Unsigned(), ty, mv$(val), mv$(and_then));
- break;
- case ::HIR::CoreType::Str:
- ASSERT_BUG(sp, node.m_branches.is_String(), "Tree for &str isn't a _String - node="<<node);
- this->generate_branches_Borrow_str(sp, node.m_default, node.m_branches.as_String(), ty, mv$(val), mv$(and_then));
- break;
- case ::HIR::CoreType::F32:
- case ::HIR::CoreType::F64:
- ASSERT_BUG(sp, node.m_branches.is_Float(), "Tree for float isn't a _Float - node="<<node);
- this->generate_branches_Float(sp, node.m_default, node.m_branches.as_Float(), ty, mv$(val), mv$(and_then));
- break;
- default:
- TODO(sp, "Primitive - " << ty);
- break;
- }
+ TU_MATCHA( (ty.m_data), (te),
+ (Infer,
+ BUG(sp, "Hit _ in type - " << ty);
),
- (Tuple,
- BUG(sp, "Decision node on tuple - node=" << node);
+ (Diverge,
+ BUG(sp, "Matching over !");
+ ),
+ (Primitive,
+ this->gen_dispatch__primitive(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk);
),
(Path,
- // This is either a struct destructure or an enum
- TU_MATCHA( (e.binding), (pbe),
- (Unbound,
- BUG(sp, "Encounterd unbound path - " << e.path);
- ),
- (Opaque,
- and_then(node);
- ),
- (Struct,
- assert(pbe);
- TU_MATCHA( (pbe->m_data), (fields),
- (Unit,
- and_then(node);
+ // Matching over a path can only happen with an enum.
+ // TODO: What about `box` destructures?
+ // - They're handled via hidden derefs
+ if( !te.binding.is_Enum() ) {
+ TU_MATCHA( (te.binding), (pbe),
+ (Unbound,
+ BUG(sp, "Encounterd unbound path - " << te.path);
),
- (Tuple,
- BUG(sp, "Decision node on tuple struct");
+ (Opaque,
+ BUG(sp, "Attempting to match over opaque type - " << ty);
),
- (Named,
- BUG(sp, "Decision node on struct");
+ (Struct,
+ const auto& str_data = pbe->m_data;
+ TU_MATCHA( (str_data), (sd),
+ (Unit,
+ BUG(sp, "Attempting to match over unit type - " << ty);
+ ),
+ (Tuple,
+ TODO(sp, "Matching on tuple-like struct?");
+ ),
+ (Named,
+ TODO(sp, "Matching on struct?");
+ )
+ )
+ ),
+ (Union,
+ TODO(sp, "Match over Union");
+ ),
+ (Enum,
)
)
- ),
- (Union,
- TODO(sp, "Decision node on Union");
- ),
- (Enum,
- ASSERT_BUG(sp, node.m_branches.is_Variant(), "Tree for enum isn't a Variant - node="<<node);
- assert(pbe);
- this->generate_branches_Enum(sp, node.m_default, node.m_branches.as_Variant(), node.m_field_path, ty, mv$(val), mv$(and_then));
- )
- )
+ }
+
+ this->gen_dispatch__enum(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk);
),
(Generic,
- and_then(node);
+ BUG(sp, "Attempting to match a generic");
),
(TraitObject,
- ERROR(sp, E0000, "Attempting to match over a trait object");
+ BUG(sp, "Attempting to match a trait object");
),
(ErasedType,
- ERROR(sp, E0000, "Attempting to match over an erased type");
+ BUG(sp, "Attempting to match an erased type");
),
(Array,
- // TODO: Slice patterns, sequential comparison/sub-match
- TODO(sp, "Match over array");
+ BUG(sp, "Attempting to match on an Array (should have been destructured)");
),
(Slice,
- ASSERT_BUG(sp, node.m_branches.is_Slice(), "Tree for [T] isn't a _Slice - node="<<node);
- this->generate_branches_Slice(sp, node.m_default, node.m_branches.as_Slice(), node.m_field_path, ty, mv$(val), mv$(and_then));
+ // TODO: Slice size matches!
+ this->gen_dispatch__slice(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk);
+ ),
+ (Tuple,
+ BUG(sp, "Match directly on tuple");
),
(Borrow,
- if( *e.inner == ::HIR::CoreType::Str ) {
- TODO(sp, "Match over &str");
- }
- else {
- BUG(sp, "Decision node on non-str/[T] borrow - " << ty);
- }
+ BUG(sp, "Match directly on borrow");
),
(Pointer,
- ERROR(sp, E0000, "Attempting to match over a pointer");
+ // TODO: Could this actually be valid?
+ BUG(sp, "Attempting to match a pointer - " << ty);
),
(Function,
- ERROR(sp, E0000, "Attempting to match over a functon pointer");
+ // TODO: Could this actually be valid?
+ BUG(sp, "Attempting to match a function pointer - " << ty);
),
(Closure,
- ERROR(sp, E0000, "Attempting to match over a closure");
+ BUG(sp, "Attempting to match a closure");
)
)
}
-void DecisionTreeGen::generate_branch(const DecisionTreeNode::Branch& branch, ::std::function<void(const DecisionTreeNode&)> cb)
-{
- assert( !branch.is_Unset() );
- if( branch.is_Terminal() ) {
- this->m_builder.end_block( ::MIR::Terminator::make_Goto( this->get_block_for_rule( branch.as_Terminal() ) ) );
- }
- else {
- assert( branch.is_Subtree() );
- const auto& subnode = *branch.as_Subtree();
-
- cb(subnode);
- }
-}
-
-::MIR::LValue DecisionTreeGen::push_compare(const Span& sp, ::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right)
-{
- return m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool,
- ::MIR::RValue::make_BinOp({ mv$(left), op, mv$(right) })
- );
-}
-
-// TODO: Unify logic for these two, and support simpler checks for sequential values
-void DecisionTreeGen::generate_branches_Signed(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Signed& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
-{
- auto ity = ty.m_data.as_Primitive();
- auto default_block = m_builder.new_bb_unlinked();
-
- // TODO: Convert into an integer switch w/ offset instead of chained comparisons
-
- for( const auto& branch : branches )
- {
- auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked());
-
- auto cmp_gt_block = m_builder.new_bb_unlinked();
- auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Int({ branch.first.start, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) );
- m_builder.set_cur_block( cmp_gt_block );
-
- auto success_block = m_builder.new_bb_unlinked();
- auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Int({ branch.first.end, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
-
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
-
- m_builder.set_cur_block( next_block );
- }
- assert( m_builder.block_active() );
-
- if( default_branch.is_Unset() ) {
- // TODO: Emit error if non-exhaustive
- m_builder.end_block( ::MIR::Terminator::make_Diverge({}) );
- }
- else {
- this->generate_branch(default_branch, and_then);
- }
-}
-
-void DecisionTreeGen::generate_branches_Unsigned(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Unsigned& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
-{
- auto ity = ty.m_data.as_Primitive();
- auto default_block = m_builder.new_bb_unlinked();
-
- // TODO: Convert into an integer switch w/ offset instead of chained comparisons
-
- for( const auto& branch : branches )
- {
- auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked());
-
- auto cmp_gt_block = m_builder.new_bb_unlinked();
- auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ branch.first.start, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) );
- m_builder.set_cur_block( cmp_gt_block );
-
- auto success_block = m_builder.new_bb_unlinked();
- auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Uint({ branch.first.end, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
-
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
-
- m_builder.set_cur_block( next_block );
- }
- assert( m_builder.block_active() );
-
- if( default_branch.is_Unset() ) {
- // TODO: Emit error if non-exhaustive
- m_builder.end_block( ::MIR::Terminator::make_Diverge({}) );
- }
- else {
- this->generate_branch(default_branch, and_then);
- }
-}
-
-void DecisionTreeGen::generate_branches_Float(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Float& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
-{
- auto ity = ty.m_data.as_Primitive();
- auto default_block = m_builder.new_bb_unlinked();
-
- for( const auto& branch : branches )
- {
- auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked());
-
- auto cmp_gt_block = m_builder.new_bb_unlinked();
- auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Float({ branch.first.start, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) );
- m_builder.set_cur_block( cmp_gt_block );
-
- auto success_block = m_builder.new_bb_unlinked();
- auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Float({ branch.first.end, ity }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
-
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
-
- m_builder.set_cur_block( next_block );
- }
- assert( m_builder.block_active() );
-
- if( default_branch.is_Unset() ) {
- ERROR(sp, E0000, "Match over floating point with no `_` arm");
- }
- else {
- this->generate_branch(default_branch, and_then);
- }
-}
-
-void DecisionTreeGen::generate_branches_Char(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Unsigned& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
+void MatchGenGrouped::gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk)
{
- auto default_block = m_builder.new_bb_unlinked();
-
- // TODO: Convert into an integer switch w/ offset instead of chained comparisons
-
- for( const auto& branch : branches )
+ auto te = ty.m_data.as_Primitive();
+ switch(te)
{
- auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked());
-
- auto cmp_gt_block = m_builder.new_bb_unlinked();
- auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ branch.first.start, ::HIR::CoreType::Char }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) );
- m_builder.set_cur_block( cmp_gt_block );
-
- auto success_block = m_builder.new_bb_unlinked();
- auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Uint({ branch.first.end, ::HIR::CoreType::Char }));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
+ case ::HIR::CoreType::Bool: {
+ ASSERT_BUG(sp, rules.size() <= 2, "More than 2 rules for boolean");
+ for(size_t i = 0; i < rules.size(); i++)
+ {
+ ASSERT_BUG(sp, rules[i][0][ofs].is_Bool(), "PatternRule for bool isn't _Bool");
+ }
+
+ // False sorts before true.
+ auto fail_bb = rules.size() == 2 ? arm_targets[ 0] : (rules[0][0][ofs].as_Bool() ? def_blk : arm_targets[0]);
+ auto succ_bb = rules.size() == 2 ? arm_targets[rules[0].size()] : (rules[0][0][ofs].as_Bool() ? arm_targets[0] : def_blk);
+
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val), succ_bb, fail_bb }) );
+ } break;
+ case ::HIR::CoreType::U8:
+ case ::HIR::CoreType::U16:
+ case ::HIR::CoreType::U32:
+ case ::HIR::CoreType::U64:
+ case ::HIR::CoreType::U128:
+ case ::HIR::CoreType::Usize:
+
+ case ::HIR::CoreType::I8:
+ case ::HIR::CoreType::I16:
+ case ::HIR::CoreType::I32:
+ case ::HIR::CoreType::I64:
+ case ::HIR::CoreType::I128:
+ case ::HIR::CoreType::Isize:
+
+ case ::HIR::CoreType::Char:
+ if( rules.size() == 1 )
+ {
+ // Special case, single option, equality only
+ const auto& r = rules[0][0][ofs];
+ ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str());
+ const auto& re = r.as_Value();
+ auto test_val = ::MIR::Param(re.clone());
+ auto cmp_lval = m_builder.get_rval_in_if_cond(sp, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), arm_targets[0], def_blk }) );
+ }
+ else
+ {
+ // TODO: Add a SwitchInt terminator for use with this. (Or just a SwitchVal terminator?)
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
+ // NOTE: Rules are currently sorted
+ // TODO: If there are Constant::Const values in the list, they need to come first! (with equality checks)
- m_builder.set_cur_block( next_block );
- }
- assert( m_builder.block_active() );
+ // Does a sorted linear search. Binary search would be nicer but is harder to implement.
+ size_t tgt_ofs = 0;
+ for(size_t i = 0; i < rules.size(); i++)
+ {
+ for(size_t j = 1; j < rules[i].size(); j ++)
+ ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match");
+
+ const auto& r = rules[i][0][ofs];
+ ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str());
+ const auto& re = r.as_Value();
+ if(re.is_Const())
+ TODO(sp, "Handle Constant::Const in match");
+
+ // IF v < tst : def_blk
+ // Skip if the previous value was the imediat predecesor
+ bool is_succ = i != 0 && (re.is_Uint()
+ ? re.as_Uint().v == rules[i-1][0][ofs].as_Value().as_Uint().v + 1
+ : re.as_Int().v == rules[i-1][0][ofs].as_Value().as_Int().v + 1
+ );
+ if( !is_succ )
+ {
+ auto cmp_eq_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_lt = this->push_compare(val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone()));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) );
+ m_builder.set_cur_block(cmp_eq_blk);
+ }
- if( default_branch.is_Unset() ) {
- // TODO: Error if not exhaustive.
- m_builder.end_block( ::MIR::Terminator::make_Diverge({}) );
- }
- else {
- this->generate_branch(default_branch, and_then);
- }
-}
-void DecisionTreeGen::generate_branches_Bool(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Bool& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
-{
- //assert( ty.m_data.is_Boolean() );
+ // IF v == tst : target
+ {
+ auto next_cmp_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_eq = this->push_compare( val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) );
+ m_builder.set_cur_block(next_cmp_blk);
+ }
- if( default_branch.is_Unset() )
- {
- if( branches.false_branch.is_Unset() || branches.true_branch.is_Unset() ) {
- // Non-exhaustive match - ERROR
- }
- }
- else
- {
- if( branches.false_branch.is_Unset() && branches.true_branch.is_Unset() ) {
- // Unreachable default (NOTE: Not an error here)
+ tgt_ofs += rules[i].size();
+ }
+ m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) );
}
- }
-
- // Emit an if based on the route taken
- auto bb_false = m_builder.new_bb_unlinked();
- auto bb_true = m_builder.new_bb_unlinked();
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val), bb_true, bb_false }) );
+ break;
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64: {
+ // NOTE: Rules are currently sorted
+ // TODO: If there are Constant::Const values in the list, they need to come first!
+ size_t tgt_ofs = 0;
+ for(size_t i = 0; i < rules.size(); i++)
+ {
+ for(size_t j = 1; j < rules[i].size(); j ++)
+ ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match");
- // Recurse into sub-patterns
- const auto& branch_false = ( !branches.false_branch.is_Unset() ? branches.false_branch : default_branch );
- const auto& branch_true = ( !branches. true_branch.is_Unset() ? branches. true_branch : default_branch );
+ const auto& r = rules[i][0][ofs];
+ ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str());
+ const auto& re = r.as_Value();
+ if(re.is_Const())
+ TODO(sp, "Handle Constant::Const in match");
- m_builder.set_cur_block(bb_true );
- this->generate_branch(branch_true , and_then);
- m_builder.set_cur_block(bb_false);
- this->generate_branch(branch_false, and_then);
-}
-
-void DecisionTreeGen::generate_branches_Borrow_str(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_String& branches,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
-{
- // TODO: Chained comparisons with ordering.
- // - Would this just emit a eBinOp? That implies deep codegen support for strings.
- // - rustc emits calls to PartialEq::eq for this and for slices. mrustc could use PartialOrd and fall back to PartialEq if unavaliable?
- // > Requires crate access here! - A memcmp call is probably better, probably via a binop
- // NOTE: The below implementation gets the final codegen to call memcmp on the strings by emitting eBinOp::{LT,GT}
-
- // - Remove the wrapping Deref (which must be there)
- ASSERT_BUG(sp, val.is_Deref(), "Match over str without a deref - " << val);
- auto tmp = mv$( *val.as_Deref().val );
- val = mv$(tmp);
-
- auto default_bb = m_builder.new_bb_unlinked();
-
- // TODO: Binary search? Perfect-Hash-Function?
- assert( !branches.empty() );
- for(const auto& branch : branches)
- {
- auto next_bb = (&branch == &branches.back() ? default_bb : m_builder.new_bb_unlinked());
+ // IF v < tst : def_blk
+ {
+ auto cmp_eq_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_lt = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) );
+ m_builder.set_cur_block(cmp_eq_blk);
+ }
- auto cmp_gt_bb = m_builder.new_bb_unlinked();
+ // IF v == tst : target
+ {
+ auto next_cmp_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_eq = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) );
+ m_builder.set_cur_block(next_cmp_blk);
+ }
+
+ tgt_ofs += rules[i].size();
+ }
+ m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) );
+ } break;
+ case ::HIR::CoreType::Str:
+ // Remove the deref on the &str
+ auto oval = mv$(val);
+ auto val = mv$(*oval.as_Deref().val);
+ // NOTE: Rules are currently sorted
+ // TODO: If there are Constant::Const values in the list, they need to come first!
+ size_t tgt_ofs = 0;
+ for(size_t i = 0; i < rules.size(); i++)
+ {
+ for(size_t j = 1; j < rules[i].size(); j ++)
+ ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match");
- auto lt_val = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant(branch.first) );
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(lt_val), default_bb, cmp_gt_bb }) );
- m_builder.set_cur_block(cmp_gt_bb);
+ const auto& r = rules[i][0][ofs];
+ ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str());
+ const auto& re = r.as_Value();
+ if(re.is_Const())
+ TODO(sp, "Handle Constant::Const in match");
- auto eq_bb = m_builder.new_bb_unlinked();
- auto gt_val = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant(branch.first) );
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(gt_val), next_bb, eq_bb }) );
- m_builder.set_cur_block(eq_bb);
+ // IF v < tst : def_blk
+ {
+ auto cmp_eq_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_lt = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) );
+ m_builder.set_cur_block(cmp_eq_blk);
+ }
- this->generate_branch(branch.second, and_then);
+ // IF v == tst : target
+ {
+ auto next_cmp_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_eq = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) );
+ m_builder.set_cur_block(next_cmp_blk);
+ }
- m_builder.set_cur_block(next_bb);
+ tgt_ofs += rules[i].size();
+ }
+ m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) );
+ break;
}
- this->generate_branch(default_branch, and_then);
}
-void DecisionTreeGen::generate_branches_Enum(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Variant& branches,
- const field_path_t& field_path,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
+void MatchGenGrouped::gen_dispatch__enum(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk)
{
- const auto& enum_ref = *ty.m_data.as_Path().binding.as_Enum();
- const auto& enum_path = ty.m_data.as_Path().path.m_data.as_Generic();
- const auto& variants = enum_ref.m_variants;
- auto variant_count = variants.size();
- bool has_any = ! default_branch.is_Unset();
-
- if( branches.size() < variant_count && ! has_any ) {
- ERROR(sp, E0000, "Non-exhaustive match over " << ty << " - " << branches.size() << " out of " << variant_count << " present");
- }
- // DISABLED: Some complex matches don't directly use some defaults
- //if( branches.size() == variant_count && has_any ) {
- // ERROR(sp, E0000, "Unreachable _ arm - " << branches.size() << " variants in " << enum_path);
- //}
-
- auto any_block = (has_any ? m_builder.new_bb_unlinked() : 0);
-
- // Emit a switch over the variant
- ::std::vector< ::MIR::BasicBlockId> variant_blocks;
- variant_blocks.reserve( variant_count );
- for( const auto& branch : branches )
+ TRACE_FUNCTION;
+ auto& te = ty.m_data.as_Path();
+ const auto& pbe = te.binding.as_Enum();
+
+ auto decison_arm = m_builder.pause_cur_block();
+
+ auto monomorph = [&](const auto& ty) {
+ auto rv = monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty);
+ m_builder.resolve().expand_associated_types(sp, rv);
+ return rv;
+ };
+ auto var_count = pbe->m_variants.size();
+ size_t arm_idx = 0;
+ ::std::vector< ::MIR::BasicBlockId> arms(var_count, def_blk);
+ for(size_t i = 0; i < rules.size(); i ++)
{
- if( variant_blocks.size() != branch.first ) {
- assert( variant_blocks.size() < branch.first );
- assert( has_any );
- variant_blocks.resize( branch.first, any_block );
+ ASSERT_BUG(sp, rules[i][0][ofs].is_Variant(), "Rule for enum isn't Any or Variant - " << rules[i][0][ofs].tag_str());
+ const auto& re = rules[i][0][ofs].as_Variant();
+ unsigned int var_idx = re.idx;
+ arms[var_idx] = m_builder.new_bb_unlinked();
+ DEBUG("Variant " << var_idx);
+
+ // Create new rule collection based on the variants
+ t_rules_subset inner_set { rules[i].size(), /*is_arm_indexes=*/false };
+ for(size_t j = 0; j < rules[i].size(); j ++)
+ {
+ const auto& r = rules[i][j];
+ inner_set.push_bb(r[ofs].as_Variant().sub_rules, arm_targets[arm_idx]);
+ arm_idx ++;
}
- variant_blocks.push_back( m_builder.new_bb_unlinked() );
- }
- if( variant_blocks.size() != variant_count )
- {
- ASSERT_BUG(sp, variant_blocks.size() < variant_count, "Branch count (" << variant_blocks.size() << ") > variant count (" << variant_count << ") in match of " << ty);
- ASSERT_BUG(sp, has_any, "Non-exhaustive match and no any arm");
- variant_blocks.resize( variant_count, any_block );
- }
- bool any_arm_used = ::std::any_of( variant_blocks.begin(), variant_blocks.end(), [any_block](const auto& blk){ return blk == any_block; } );
+ ::HIR::TypeRef fake_tup;
- m_builder.end_block( ::MIR::Terminator::make_Switch({
- val.clone(), variant_blocks // NOTE: Copies the list, so it can be used lower down
- }) );
-
- // Emit sub-patterns, looping over variants
- for( const auto& branch : branches )
- {
- auto bb = variant_blocks[branch.first];
- const auto& var = variants[branch.first];
- DEBUG(branch.first << " " << var.first << " = " << branch.second);
-
- auto var_lval = ::MIR::LValue::make_Downcast({ box$(val.clone()), branch.first });
-
- ::HIR::TypeRef fake_ty;
-
- TU_MATCHA( (var.second), (e),
+ const auto& var_data = pbe->m_variants.at(re.idx).second;
+ TU_MATCHA( (var_data), (ve),
(Unit,
- DEBUG("- Unit");
+ // Nothing to recurse
),
(Value,
- DEBUG("- Value");
+ // Nothing to recurse
),
(Tuple,
- // Make a fake tuple
- ::std::vector< ::HIR::TypeRef> ents;
- for( const auto& fld : e )
+ // Create a dummy tuple to contain the inner types.
+ ::std::vector< ::HIR::TypeRef> fake_ty_ents;
+ fake_ty_ents.reserve( ve.size() );
+ for(unsigned int i = 0; i < ve.size(); i ++)
{
- ents.push_back( monomorphise_type(sp, enum_ref.m_params, enum_path.m_params, fld.ent) );
+ fake_ty_ents.push_back( monomorph(ve[i].ent) );
}
- fake_ty = ::HIR::TypeRef( mv$(ents) );
- m_builder.resolve().expand_associated_types(sp, fake_ty);
- DEBUG("- Tuple - " << fake_ty);
+ fake_tup = ::HIR::TypeRef( mv$(fake_ty_ents) );
),
(Struct,
- ::std::vector< ::HIR::TypeRef> ents;
- for( const auto& fld : e )
+ // Create a dummy tuple to contain the inner types.
+ ::std::vector< ::HIR::TypeRef> fake_ty_ents;
+ fake_ty_ents.reserve( ve.size() );
+ for(unsigned int i = 0; i < ve.size(); i ++)
{
- ents.push_back( monomorphise_type(sp, enum_ref.m_params, enum_path.m_params, fld.second.ent) );
+ fake_ty_ents.push_back( monomorph(ve[i].second.ent) );
}
- fake_ty = ::HIR::TypeRef( mv$(ents) );
- m_builder.resolve().expand_associated_types(sp, fake_ty);
- DEBUG("- Struct - " << fake_ty);
+ fake_tup = ::HIR::TypeRef( mv$(fake_ty_ents) );
)
)
- m_builder.set_cur_block( bb );
- if( fake_ty == ::HIR::TypeRef() || fake_ty.m_data.as_Tuple().size() == 0 ) {
- this->generate_branch(branch.second, and_then);
- }
- else {
- this->generate_branch(branch.second, [&](auto& subnode) {
- // Call special handler to determine when the enum is over
- this->generate_tree_code__enum(sp, subnode, fake_ty, var_lval, field_path, and_then);
- });
- }
- }
+ m_builder.set_cur_block(arms[var_idx]);
+ // Recurse with the new ruleset
+ // - On success, these should jump to targets[i]
- if( any_arm_used )
- {
- DEBUG("_ = " << default_branch);
- if( !default_branch.is_Unset() )
- {
- m_builder.set_cur_block(any_block);
- this->generate_branch(default_branch, and_then);
- }
- }
- else
- {
- DEBUG("_ = UNUSED - " << default_branch);
+ auto new_lval = ::MIR::LValue::make_Downcast({ box$(val.clone()), var_idx });
+ auto inst = MatchGenGrouped { m_builder, sp, fake_tup, new_lval, {}, rules[0][0][ofs].field_path.size() };
+ inst.gen_for_slice(inner_set, 0, def_blk);
+ ASSERT_BUG(sp, ! m_builder.block_active(), "Block still active after enum match generation");
}
+
+ m_builder.set_cur_block(decison_arm);
+ m_builder.end_block( ::MIR::Terminator::make_Switch({ mv$(val), mv$(arms) }) );
}
-void DecisionTreeGen::generate_branches_Slice(
- const Span& sp,
- const DecisionTreeNode::Branch& default_branch,
- const DecisionTreeNode::Values::Data_Slice& branches,
- const field_path_t& field_path,
- const ::HIR::TypeRef& ty, ::MIR::LValue val,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
+void MatchGenGrouped::gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk)
{
- if( default_branch.is_Unset() ) {
- ERROR(sp, E0000, "Non-exhaustive match over " << ty);
- }
-
auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() }));
- // NOTE: Un-deref the slice
- ASSERT_BUG(sp, val.is_Deref(), "slice matches must be passed a deref");
- auto tmp = mv$( *val.as_Deref().val );
- val = mv$(tmp);
+ // TODO: Re-sort the rules list to interleve Constant::Bytes and Slice
- auto any_block = m_builder.new_bb_unlinked();
-
- // TODO: Select one of three ways of picking the arm:
- // - Integer switch (unimplemented)
- // - Binary search
- // - Sequential comparisons
-
- // TODO: Binary search instead?
- for( const auto& branch : branches.fixed_arms )
+ // Just needs to check the lengths, then dispatch.
+ size_t tgt_ofs = 0;
+ for(size_t i = 0; i < rules.size(); i++)
{
- auto val_des = ::MIR::Constant::make_Uint({ static_cast<uint64_t>(branch.first), ::HIR::CoreType::Usize });
+ //for(size_t j = 1; j < rules[i].size(); j ++)
+ // ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Slice match");
- // Special case - final just does equality
- if( &branch == &branches.fixed_arms.back() )
+ const auto& r = rules[i][0][ofs];
+ if(const auto* re = r.opt_Slice())
{
- auto val_cmp_eq = push_compare(sp, val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_des));
+ auto val_tst = ::MIR::Constant::make_Uint({ re->len, ::HIR::CoreType::Usize });
- auto success_block = m_builder.new_bb_unlinked();
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_eq), success_block, any_block }) );
+ // IF v < tst : target
+ if( re->len > 0 )
+ {
+ auto cmp_eq_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_lt = this->push_compare( val_len.clone(), ::MIR::eBinOp::LT, val_tst.clone() );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) );
+ m_builder.set_cur_block(cmp_eq_blk);
+ }
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
+ // IF v == tst : target
+ {
+ auto succ_blk = m_builder.new_bb_unlinked();
+ auto next_cmp_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_eq = this->push_compare( val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_tst) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), succ_blk, next_cmp_blk }) );
+ m_builder.set_cur_block(succ_blk);
+
+ //
+ t_rules_subset inner_set { rules[i].size(), /*is_arm_indexes=*/false };
+ for(size_t j = 0; j < rules[i].size(); j ++)
+ {
+ const auto& r = rules[i][j];
+ inner_set.push_bb(r[ofs].as_Slice().sub_rules, arm_targets[tgt_ofs + j]);
+ }
+ auto inst = MatchGenGrouped { m_builder, sp, ty, val, {}, rules[0][0][ofs].field_path.size() };
+ inst.gen_for_slice(inner_set, 0, def_blk);
- m_builder.set_cur_block( any_block );
+ m_builder.set_cur_block(next_cmp_blk);
+ }
}
- // Special case for zero (which can't have a LT)
- else if( branch.first == 0 )
+ else if(const auto* re = r.opt_Value())
{
- auto next_block = m_builder.new_bb_unlinked();
- auto val_cmp_eq = push_compare(sp, val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_des));
+ ASSERT_BUG(sp, re->is_Bytes(), "Slice with non-Bytes value - " << *re);
+ const auto& b = re->as_Bytes();
+
+ auto val_tst = ::MIR::Constant::make_Uint({ b.size(), ::HIR::CoreType::Usize });
+ auto cmp_slice_val = m_builder.lvalue_or_temp(sp,
+ ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ::HIR::TypeRef::new_slice(::HIR::CoreType::U8) ),
+ ::MIR::RValue::make_MakeDst({ ::MIR::Param(re->clone()), val_tst.clone() })
+ );
+
+ if( b.size() > 0 )
+ {
+ auto cmp_eq_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_lt = this->push_compare( val_len.clone(), ::MIR::eBinOp::LT, val_tst.clone() );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) );
+ m_builder.set_cur_block(cmp_eq_blk);
+ }
- auto success_block = m_builder.new_bb_unlinked();
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_eq), success_block, next_block }) );
+ // IF v == tst : target
+ {
+ auto succ_blk = m_builder.new_bb_unlinked();
+ auto next_cmp_blk = m_builder.new_bb_unlinked();
+ auto cmp_lval_eq = this->push_compare( val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_tst) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), succ_blk, next_cmp_blk }) );
+ m_builder.set_cur_block(succ_blk);
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
+ // TODO: What if `val` isn't a Deref?
+ ASSERT_BUG(sp, val.is_Deref(), "TODO: Handle non-Deref matches of byte strings");
+ cmp_lval_eq = this->push_compare( val.as_Deref().val->clone(), ::MIR::eBinOp::EQ, mv$(cmp_slice_val) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], def_blk }) );
- m_builder.set_cur_block( next_block );
+ m_builder.set_cur_block(next_cmp_blk);
+ }
}
- // General case, with two comparisons
else
{
- auto next_block = m_builder.new_bb_unlinked();
-
- auto cmp_gt_block = m_builder.new_bb_unlinked();
- auto val_cmp_lt = push_compare(sp, val_len.clone(), ::MIR::eBinOp::LT, val_des.clone());
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), any_block, cmp_gt_block }) );
- m_builder.set_cur_block( cmp_gt_block );
-
- auto success_block = m_builder.new_bb_unlinked();
- auto val_cmp_gt = push_compare(sp, val_len.clone(), ::MIR::eBinOp::GT, mv$(val_des));
- m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
-
- m_builder.set_cur_block( success_block );
- this->generate_branch(branch.second, and_then);
-
- m_builder.set_cur_block( next_block );
+ BUG(sp, "Matching without _Slice pattern - " << r.tag_str() << " - " << r);
}
- }
- assert( m_builder.block_active() );
- if( default_branch.is_Unset() ) {
- // TODO: Emit error if non-exhaustive
- m_builder.end_block( ::MIR::Terminator::make_Diverge({}) );
- }
- else {
- this->generate_branch(default_branch, and_then);
+ tgt_ofs += rules[i].size();
}
+ m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) );
}
-namespace {
- bool path_starts_with(const field_path_t& test, const field_path_t& prefix)
+
+void MatchGenGrouped::gen_dispatch_range(const field_path_t& field_path, const ::MIR::Constant& first, const ::MIR::Constant& last, ::MIR::BasicBlockId def_blk)
+{
+ TRACE_FUNCTION_F("field_path="<<field_path<<", " << first << " ... " << last);
+ ::MIR::LValue val;
+ ::HIR::TypeRef ty;
+ get_ty_and_val(sp, m_builder.resolve(), m_top_ty, m_top_val, field_path, m_field_path_ofs, ty, val);
+ DEBUG("ty = " << ty << ", val = " << val);
+
+ if( const auto* tep = ty.m_data.opt_Primitive() )
{
- //DEBUG("test="<<test<<", prefix="<<prefix);
- if( test.size() < prefix.size() )
+ auto te = *tep;
+
+ bool lower_possible = true;
+ bool upper_possible = true;
+
+ switch(te)
{
- return false;
+ case ::HIR::CoreType::Bool:
+ BUG(sp, "Range match over Bool");
+ break;
+ case ::HIR::CoreType::Str:
+ BUG(sp, "Range match over Str - is this valid?");
+ break;
+ case ::HIR::CoreType::U8:
+ case ::HIR::CoreType::U16:
+ case ::HIR::CoreType::U32:
+ case ::HIR::CoreType::U64:
+ case ::HIR::CoreType::U128:
+ case ::HIR::CoreType::Usize:
+ lower_possible = (first.as_Uint().v > 0);
+ // TODO: Should this also check for the end being the max value of the type?
+ // - Can just leave that to the optimiser
+ upper_possible = true;
+ break;
+ case ::HIR::CoreType::I8:
+ case ::HIR::CoreType::I16:
+ case ::HIR::CoreType::I32:
+ case ::HIR::CoreType::I64:
+ case ::HIR::CoreType::I128:
+ case ::HIR::CoreType::Isize:
+ lower_possible = true;
+ upper_possible = true;
+ break;
+ case ::HIR::CoreType::Char:
+ lower_possible = (first.as_Uint().v > 0);
+ upper_possible = (first.as_Uint().v <= 0x10FFFF);
+ break;
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
+ // NOTE: No upper or lower limits
+ break;
}
- else if( ! ::std::equal(prefix.data.begin(), prefix.data.end(), test.data.begin()) )
+
+ if( lower_possible )
{
- return false;
+ auto test_bb_2 = m_builder.new_bb_unlinked();
+ // IF `val` < `first` : fail_bb
+ auto cmp_lt_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, ::MIR::Param(first.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), def_blk, test_bb_2 }) );
+
+ m_builder.set_cur_block(test_bb_2);
}
- else
+
+
+ if( upper_possible )
{
- return true;
+ auto succ_bb = m_builder.new_bb_unlinked();
+
+ // IF `val` > `last` : fail_bb
+ auto cmp_gt_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, ::MIR::Param(last.clone()) }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), def_blk, succ_bb }) );
+
+ m_builder.set_cur_block(succ_bb);
}
}
+ else
+ {
+ TODO(sp, "ValueRange on " << ty);
+ }
}
-
-void DecisionTreeGen::generate_tree_code__enum(
- const Span& sp,
- const DecisionTreeNode& node, const ::HIR::TypeRef& fake_ty, const ::MIR::LValue& val,
- const field_path_t& path_prefix,
- ::std::function<void(const DecisionTreeNode&)> and_then
- )
+void MatchGenGrouped::gen_dispatch_splitslice(const field_path_t& field_path, const PatternRule::Data_SplitSlice& e, ::MIR::BasicBlockId def_blk)
{
- if( ! path_starts_with(node.m_field_path, path_prefix) )
+ TRACE_FUNCTION_F("field_path="<<field_path<<", [" << e.leading << ", .., " << e.trailing << "]");
+ ::MIR::LValue val;
+ ::HIR::TypeRef ty;
+ get_ty_and_val(sp, m_builder.resolve(), m_top_ty, m_top_val, field_path, m_field_path_ofs, ty, val);
+ DEBUG("ty = " << ty << ", val = " << val);
+
+ ASSERT_BUG(sp, ty.m_data.is_Slice(), "SplitSlice pattern on non-slice - " << ty);
+
+ // Obtain slice length
+ auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() }));
+
+ // 1. Check that length is sufficient for the pattern to be used
+ // `IF len < min_len : def_blk, next
{
- and_then(node);
+ auto next = m_builder.new_bb_unlinked();
+ auto cmp_val = this->push_compare(val_len.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ e.min_len, ::HIR::CoreType::Usize }));
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_val), def_blk, next }) );
+ m_builder.set_cur_block(next);
}
- else
+
+ // 2. Recurse into leading patterns.
+ if( !e.leading.empty() )
{
- this->generate_tree_code(sp, node, fake_ty, path_prefix.size(), val,
- [&](const auto& next_node) {
- if( ! path_starts_with(next_node.m_field_path, path_prefix) )
- {
- and_then(next_node);
- }
- else
- {
- this->generate_tree_code__enum(sp, next_node, fake_ty, val, path_prefix, and_then);
- }
- });
+ auto next = m_builder.new_bb_unlinked();
+ auto inner_set = t_rules_subset { 1, /*is_arm_indexes=*/false };
+ inner_set.push_bb( e.leading, next );
+ auto inst = MatchGenGrouped { m_builder, sp, ty, val, {}, field_path.size() };
+ inst.gen_for_slice(inner_set, 0, def_blk);
+
+ m_builder.set_cur_block(next);
+ }
+
+ if( !e.trailing.empty() )
+ {
+ TODO(sp, "Handle trailing rules in SplitSlice - " << e.trailing);
}
}