eeadb6082f44 — Leonard Ritter 7 days ago
* SPIR-V: fixed unstructured branch validation error for `if/elseif/else` construct
* retired `value-kind-if` node in favor of `value-kind-cond-template`
* removed `sc_if_*` API functions
* added `sc_cond_new` API function
M include/scopes/scopes.h +1 -3
@@ 237,9 237,7 @@ SCOPES_LIBEXPORT sc_symbol_raises_t sc_g
 SCOPES_LIBEXPORT sc_valueref_t sc_global_string_new(const char *ptr, size_t count);
 SCOPES_LIBEXPORT sc_valueref_t sc_global_string_new_from_cstr(const char *ptr);
 
-SCOPES_LIBEXPORT sc_valueref_t sc_if_new();
-SCOPES_LIBEXPORT void sc_if_append_then_clause(sc_valueref_t value, sc_valueref_t cond, sc_valueref_t body);
-SCOPES_LIBEXPORT void sc_if_append_else_clause(sc_valueref_t value, sc_valueref_t body);
+SCOPES_LIBEXPORT sc_valueref_t sc_cond_new(sc_valueref_t cond, sc_valueref_t then_value, sc_valueref_t else_value);
 
 SCOPES_LIBEXPORT sc_valueref_t sc_switch_new(sc_valueref_t expr);
 SCOPES_LIBEXPORT void sc_switch_append_case(sc_valueref_t value, sc_valueref_t literal, sc_valueref_t body);

          
M lib/scopes/core.sc +20 -23
@@ 2008,14 2008,11 @@ fn dispatch-and-or (args flip)
         return call-elsef
     else
         return thenargs
-    let ifval = (sc_if_new)
-    if flip
-        sc_if_append_then_clause ifval condbool call-elsef
-        sc_if_append_else_clause ifval thenargs
-    else
-        sc_if_append_then_clause ifval condbool thenargs
-        sc_if_append_else_clause ifval call-elsef
-    ifval
+    sc_cond_new condbool
+        if flip
+            _ call-elsef thenargs
+        else
+            _ thenargs call-elsef
 
 let safe-shl =
     spice-macro

          
@@ 4610,13 4607,14 @@ let packedtupleof = (gen-tupleof sc_pack
                 fn (args)
                     let self = ('getarg args 0)
                     let other = ('getarg args 1)
-                    let block = (sc_if_new)
-                    for i in (range ('element-count ('typeof self)))
-                        sc_if_append_then_clause block
-                            `((self @ i) != (other @ i))
-                            `false
-                    sc_if_append_else_clause block `true
-                    block
+                    loop (i result = ('element-count ('typeof self)) `true)
+                        if (i == 0)
+                            break result
+                        let i = (i - 1)
+                        _   i
+                            sc_cond_new `((self @ i) != (other @ i))
+                                `false
+                                result
     __unpack = __unpack-aggregate
     __countof = __countof-aggregate
     __@ =

          
@@ 7339,15 7337,14 @@ fn constructor (cls args...)
 spice tuple== (self other)
     let cls = ('typeof self)
     let numfields = ('element-count cls)
-    let block = (sc_if_new)
     let quoted-false = `false
-    loop (i = 0)
-        if (i == numfields)
-            break;
-        sc_if_append_then_clause block `((@ self i) != (@ other i)) quoted-false
-        i + 1
-    sc_if_append_else_clause block `true
-    block
+    loop (i result = numfields `true)
+        if (i == 0)
+            break result
+        let i = (i - 1)
+        _   i
+            sc_cond_new `((self @ i) != (other @ i)) quoted-false result
+
 
 typedef+ tuple
     spice-quote

          
M src/expander.cpp +22 -18
@@ 837,10 837,23 @@ struct Expander {
             branches.push_back(EOL);
         }
 
-        auto ifexpr = If::from();
+        ValueRef result;
 
-        int lastidx = (int)branches.size() - 1;
-        for (int idx = 0; idx < lastidx; ++idx) {
+        const int lastidx = (int)branches.size() - 1;
+        it = branches[lastidx];
+        if (it == EOL) {
+            result = ValueRef(ref(anchor, ArgumentList::from({})));
+        } else {
+            auto else_anchor = it->at.anchor();
+            it = it->next;
+            Expander subexp(Scope::from(nullptr, env), astscope);
+
+            result = SCOPES_GET_RESULT(
+                subexp.expand_expression(ref(else_anchor, it), false));
+        }
+
+        int idx = lastidx;
+        while (idx-- > 0) {
             it = branches[idx];
             //const Anchor *anchor = it->at->anchor();
             SCOPES_CHECK_RESULT(verify_list_parameter_count("branch", it, 1, -1));

          
@@ 873,24 886,15 @@ struct Expander {
                 env = subexp.env;
 
                 subexp.env = Scope::from(nullptr, env);
-                ifexpr->append_then(cond,
-                    SCOPES_GET_RESULT(
-                        subexp.expand_expression(ref(branch_anchor, it), false)));
+                result =
+                    ref(branch_anchor, CondTemplate::from(cond,
+                        SCOPES_GET_RESULT(
+                            subexp.expand_expression(ref(branch_anchor, it), false)),
+                        result));
             }
         }
 
-        it = branches[lastidx];
-        if (it != EOL) {
-            auto else_anchor = it->at.anchor();
-            it = it->next;
-            Expander subexp(Scope::from(nullptr, env), astscope);
-
-            ifexpr->append_else(
-                SCOPES_GET_RESULT(
-                    subexp.expand_expression(ref(else_anchor, it), false)));
-        }
-
-        return ValueRef(ref(anchor, ifexpr));
+        return ValueRef(ref(anchor, result));
     }
 
     static SCOPES_RESULT(bool) get_kwargs(

          
M src/globals.cpp +3 -13
@@ 1448,17 1448,9 @@ sc_valueref_t sc_global_string_new_from_
     return GlobalString::from(ptr, strlen(ptr));
 }
 
-sc_valueref_t sc_if_new() {
-    using namespace scopes;
-    return If::from();
-}
-void sc_if_append_then_clause(sc_valueref_t value, sc_valueref_t cond, sc_valueref_t body) {
+sc_valueref_t sc_cond_new(sc_valueref_t cond, sc_valueref_t then_value, sc_valueref_t else_value) {
     using namespace scopes;
-    value.cast<If>()->append_then(cond, body);
-}
-void sc_if_append_else_clause(sc_valueref_t value, sc_valueref_t body) {
-    using namespace scopes;
-    value.cast<If>()->append_else(body);
+    return CondTemplate::from(cond, then_value, else_value);
 }
 
 sc_valueref_t sc_switch_new(sc_valueref_t expr) {

          
@@ 2375,9 2367,7 @@ void init_globals(int argc, char *argv[]
     DEFINE_RAISING_EXTERN_C_FUNCTION(sc_global_storage_class, TYPE_Symbol, TYPE_ValueRef);
     DEFINE_EXTERN_C_FUNCTION(sc_global_string_new, TYPE_ValueRef, native_ro_pointer_type(TYPE_I8), TYPE_USize);
     DEFINE_EXTERN_C_FUNCTION(sc_global_string_new_from_cstr, TYPE_ValueRef, native_ro_pointer_type(TYPE_I8));
-    DEFINE_EXTERN_C_FUNCTION(sc_if_new, TYPE_ValueRef);
-    DEFINE_EXTERN_C_FUNCTION(sc_if_append_then_clause, _void, TYPE_ValueRef, TYPE_ValueRef, TYPE_ValueRef);
-    DEFINE_EXTERN_C_FUNCTION(sc_if_append_else_clause, _void, TYPE_ValueRef, TYPE_ValueRef);
+    DEFINE_EXTERN_C_FUNCTION(sc_cond_new, TYPE_ValueRef, TYPE_ValueRef, TYPE_ValueRef, TYPE_ValueRef);
     DEFINE_EXTERN_C_FUNCTION(sc_switch_new, TYPE_ValueRef, TYPE_ValueRef);
     DEFINE_EXTERN_C_FUNCTION(sc_switch_append_case, _void, TYPE_ValueRef, TYPE_ValueRef, TYPE_ValueRef);
     DEFINE_EXTERN_C_FUNCTION(sc_switch_append_pass, _void, TYPE_ValueRef, TYPE_ValueRef, TYPE_ValueRef);

          
M src/globals.hpp +1 -3
@@ 67,9 67,7 @@ namespace scopes {
     T(g_sc_expression_new, "sc_expression_new") \
     T(g_sc_expression_append, "sc_expression_append") \
     T(g_sc_expression_set_scoped, "sc_expression_set_scoped") \
-    T(g_sc_if_new, "sc_if_new") \
-    T(g_sc_if_append_then_clause, "sc_if_append_then_clause") \
-    T(g_sc_if_append_else_clause, "sc_if_append_else_clause") \
+    T(g_sc_cond_new, "sc_cond_new") \
     T(g_sc_switch_new, "sc_switch_new") \
     T(g_sc_switch_append_case, "sc_switch_append_case") \
     T(g_sc_switch_append_pass, "sc_switch_append_pass") \

          
M src/prover.cpp +32 -57
@@ 3509,67 3509,42 @@ static SCOPES_RESULT(TypedValueRef) prov
     return finalize_merge_label(ctx, merge_label, "switch case");
 }
 
-static SCOPES_RESULT(TypedValueRef) prove_If(const ASTContext &ctx, const IfRef &_if) {
+static SCOPES_RESULT(TypedValueRef) prove_CondTemplate(const ASTContext &ctx, const CondTemplateRef &node) {
     SCOPES_RESULT_TYPE(TypedValueRef);
-    assert(!_if->clauses.empty());
-    int numclauses = _if->clauses.size();
-    assert(numclauses >= 1);
-    CondBrRef first_condbr;
-    CondBrRef last_condbr;
-    LabelRef merge_label = make_merge_label(ctx, _if.anchor());
+    LabelRef merge_label = make_merge_label(ctx, node.anchor());
     ASTContext subctx = ctx.with_block(merge_label->body);
-    for (int i = 0; i < numclauses; ++i) {
-        auto &&clause = _if->clauses[i];
-        //assert(clause.anchor);
-        //SCOPES_ANCHOR(clause.anchor);
-        if (clause.is_then()) {
-            TypedValueRef newcond = SCOPES_GET_RESULT(prove(subctx, clause.cond));
-            {
-                SCOPES_TRACE_PROVE_ARG(newcond);
-                newcond = ref(newcond.anchor(),
-                    ExtractArgument::from(newcond, 0));
-                SCOPES_CHECK_RESULT(build_tobool(subctx, clause.cond.anchor(), newcond));
-                SCOPES_CHECK_RESULT(build_deref_automove(subctx, newcond, newcond));
-                auto condT = strip_qualifiers(newcond->get_type());
-                if (condT != TYPE_Bool) {
-                    SCOPES_ERROR(ConditionNotBool, newcond->get_type());
-                }
-            }
-            CondBrRef condbr = ref(newcond.anchor(), CondBr::from(newcond));
-            if (!first_condbr) {
-                first_condbr = condbr;
-                merge_label->splitpoints.insert(condbr.unref());
-            }
-            condbr->then_body.set_parent(&merge_label->body);
-            condbr->else_body.set_parent(&merge_label->body);
-            auto thenctx = subctx.with_block(condbr->then_body);
-            auto thenresult = SCOPES_GET_RESULT(prove(thenctx, clause.value));
-            if (is_returning(thenresult->get_type())) {
-                SCOPES_CHECK_RESULT(make_merge(thenctx, thenresult.anchor(), merge_label, thenresult));
-            }
-            if (last_condbr) {
-                last_condbr->else_body.append(condbr);
 
-            } else {
-                merge_label->body.append(condbr);
-            }
-            last_condbr = condbr;
-            subctx = subctx.with_block(condbr->else_body);
-        } else {
-            assert(last_condbr);
-            auto elseresult = SCOPES_GET_RESULT(prove(subctx, clause.value));
-            if (is_returning(elseresult->get_type())) {
-                ASTContext elsectx = ctx.with_block(last_condbr->else_body);
-                SCOPES_CHECK_RESULT(make_merge(elsectx, _if.anchor(), merge_label, elseresult));
-            }
-            last_condbr = CondBrRef();
+    //assert(clause.anchor);
+    //SCOPES_ANCHOR(clause.anchor);
+    TypedValueRef newcond = SCOPES_GET_RESULT(prove(subctx, node->cond));
+    {
+        SCOPES_TRACE_PROVE_ARG(newcond);
+        newcond = ref(newcond.anchor(),
+            ExtractArgument::from(newcond, 0));
+        SCOPES_CHECK_RESULT(build_tobool(subctx, node->cond.anchor(), newcond));
+        SCOPES_CHECK_RESULT(build_deref_automove(subctx, newcond, newcond));
+        auto condT = strip_qualifiers(newcond->get_type());
+        if (condT != TYPE_Bool) {
+            SCOPES_ERROR(ConditionNotBool, newcond->get_type());
         }
     }
-    if (last_condbr) {
-        // last else value missing
-        ASTContext elsectx = ctx.with_block(last_condbr->else_body);
-        SCOPES_CHECK_RESULT(make_merge(elsectx, _if.anchor(), merge_label,
-            ref(_if.anchor(), ArgumentList::from({}))));
+    CondBrRef condbr = ref(newcond.anchor(), CondBr::from(newcond));
+    merge_label->splitpoints.insert(condbr.unref());
+    condbr->then_body.set_parent(&merge_label->body);
+    condbr->else_body.set_parent(&merge_label->body);
+    auto thenctx = subctx.with_block(condbr->then_body);
+    auto thenresult = SCOPES_GET_RESULT(prove(thenctx, node->then_value));
+    if (is_returning(thenresult->get_type())) {
+        SCOPES_CHECK_RESULT(make_merge(thenctx, thenresult.anchor(), merge_label, thenresult));
+    }
+    merge_label->body.append(condbr);
+
+    subctx = subctx.with_block(condbr->else_body);
+
+    auto elseresult = SCOPES_GET_RESULT(prove(subctx, node->else_value));
+    if (is_returning(elseresult->get_type())) {
+        ASTContext elsectx = ctx.with_block(condbr->else_body);
+        SCOPES_CHECK_RESULT(make_merge(elsectx, node.anchor(), merge_label, elseresult));
     }
 
     if (merge_label->merges.empty()) {

          
@@ 3577,7 3552,7 @@ static SCOPES_RESULT(TypedValueRef) prov
         // conditions do not need a merge label
         assert(ctx.block);
         ctx.merge_block(merge_label->body);
-        return TypedValueRef(first_condbr);
+        return TypedValueRef(condbr);
     }
 
     return finalize_merge_label(ctx, merge_label, "branch");

          
M src/quote.cpp +6 -15
@@ 230,23 230,14 @@ struct Quoter {
         return canonicalize(expr);
     }
 
-    SCOPES_RESULT(ValueRef) quote_If(int level, const IfRef &node) {
+    SCOPES_RESULT(ValueRef) quote_CondTemplate(int level, const CondTemplateRef &node) {
         SCOPES_RESULT_TYPE(ValueRef);
         auto _anchor = node.anchor();
-        auto value = REF(CallTemplate::from(g_sc_if_new, {}));
-        auto expr = REF(Expression::unscoped_from());
-        for (auto &&clause : node->clauses) {
-            if (clause.is_then()) {
-                expr->append(REF(CallTemplate::from(g_sc_if_append_then_clause, { value,
-                    SCOPES_GET_RESULT(quote(level, clause.cond)),
-                    SCOPES_GET_RESULT(quote(level, clause.value)) })));
-            } else {
-                expr->append(REF(CallTemplate::from(g_sc_if_append_else_clause, { value,
-                    SCOPES_GET_RESULT(quote(level, clause.value)) })));
-            }
-        }
-        expr->append(value);
-        return canonicalize(expr);
+        return ValueRef(REF(CallTemplate::from(g_sc_cond_new, {
+            SCOPES_GET_RESULT(quote(level, node->cond)),
+            SCOPES_GET_RESULT(quote(level, node->then_value)),
+            SCOPES_GET_RESULT(quote(level, node->else_value))
+        })));
     }
 
     SCOPES_RESULT(ValueRef) quote_Template(int level, const TemplateRef &node) {

          
M src/stream_expr.cpp +4 -11
@@ 411,18 411,11 @@ HANDLER(Unquote) {
     );
 }
 
-HANDLER(If) {
+HANDLER(CondTemplate) {
     auto _anchor = node.anchor();
     const List *l = EOL;
-    int i = node->clauses.size();
-    while (i-- > 0) {
-        auto clause = node->clauses[i];
-        if (clause.cond) {
-            l = List::from(LIST(SYMBOL(KW_Case), clause.cond, clause.value), l);
-        } else {
-            l = List::from(LIST(SYMBOL(KW_Default), clause.value), l);
-        }
-    }
+    l = List::from(LIST(SYMBOL(KW_Case), node->cond, node->then_value), l);
+    l = List::from(LIST(SYMBOL(KW_Default), node->else_value), l);
     l = List::from(SYMBOL(KW_If), l);
     return ValueRef(_anchor, ConstPointer::list_from(l));
 }

          
@@ 508,7 501,7 @@ ValueRef _convert(const ValueRef &node) 
     //CASE_HANDLER(Call)
     CASE_HANDLER(Template)
     CASE_HANDLER(Global)
-    CASE_HANDLER(If)
+    CASE_HANDLER(CondTemplate)
     CASE_HANDLER(ParameterTemplate)
     CASE_HANDLER(Expression)
     CASE_HANDLER(LabelTemplate)

          
M src/value.cpp +7 -24
@@ 948,32 948,15 @@ CondBrRef CondBr::from(const TypedValueR
 
 //------------------------------------------------------------------------------
 
-bool If::Clause::is_then() const {
-    return cond.unref() != nullptr;
-}
-
-If::If(const Clauses &_clauses)
-    : UntypedValue(VK_If), clauses(_clauses) {
-}
-
-IfRef If::from(const Clauses &_clauses) {
-    return ref(unknown_anchor(), new If(_clauses));
+CondTemplate::CondTemplate(
+    const ValueRef &_cond, const ValueRef &_then_value, const ValueRef &_else_value)
+    : UntypedValue(VK_CondTemplate),
+    cond(_cond), then_value(_then_value), else_value(_else_value) {
 }
 
-void If::append_then(const ValueRef &cond, const ValueRef &value) {
-    assert(cond);
-    assert(value);
-    Clause clause;
-    clause.cond = cond;
-    clause.value = value;
-    clauses.push_back(clause);
-}
-
-void If::append_else(const ValueRef &value) {
-    assert(value);
-    Clause clause;
-    clause.value = value;
-    clauses.push_back(clause);
+CondTemplateRef CondTemplate::from(
+    const ValueRef &cond, const ValueRef &then_value, const ValueRef &else_value) {
+    return ref(unknown_anchor(), new CondTemplate(cond, then_value, else_value));
 }
 
 //------------------------------------------------------------------------------

          
M src/value.hpp +6 -18
@@ 330,28 330,16 @@ struct CondBr : Instruction {
 
 //------------------------------------------------------------------------------
 
-struct If : UntypedValue {
-    struct Clause {
-        ValueRef cond;
-        ValueRef value;
-
-        Clause() {}
-
-        bool is_then() const;
-    };
-
-    typedef std::vector<Clause> Clauses;
-
+struct CondTemplate : UntypedValue {
     static bool classof(const Value *T);
 
-    If(const Clauses &clauses);
+    CondTemplate(const ValueRef &cond, const ValueRef &then_value, const ValueRef &else_value);
 
-    static IfRef from(const Clauses &clauses = {});
+    static CondTemplateRef from(const ValueRef &cond, const ValueRef &then_value, const ValueRef &else_value);
 
-    void append_then(const ValueRef &cond, const ValueRef &value);
-    void append_else(const ValueRef &value);
-
-    Clauses clauses;
+    ValueRef cond;
+    ValueRef then_value;
+    ValueRef else_value;
 };
 
 //------------------------------------------------------------------------------

          
M src/value_kind.hpp +1 -1
@@ 19,7 19,7 @@ namespace scopes {
     T(VK_Quote, "value-kind-quote", Quote) \
     T(VK_Unquote, "value-kind-unquote", Unquote) \
     T(VK_CompileStage, "value-kind-compile-stage", CompileStage) \
-    T(VK_If, "value-kind-if", If) \
+    T(VK_CondTemplate, "value-kind-cond-template", CondTemplate) \
     T(VK_SwitchTemplate, "value-kind-switch-template", SwitchTemplate) \
     T(VK_MergeTemplate, "value-kind-merge-template", MergeTemplate) \
     T(VK_CallTemplate, "value-kind-call-template", CallTemplate) \

          
M testing/test_spirv_loop.sc +6 -3
@@ 5,10 5,13 @@ fn function-with-exception ()
             raise;
     10
 
+fn unconst (x) x
+
 fn main ()
-    #if true 1
-    #elseif true 2
-    #else 3
+    if true 1
+    elseif ((let k = (unconst 1)) == 1)
+        k
+    else 3
     try
         let count = (function-with-exception)
         for i j in (zip (range count) (range 16))