about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2011-04-19 15:22:57 -0700
committerPatrick Walton <pcwalton@mimiga.net>2011-04-19 15:29:27 -0700
commit0ee997cee6dc6f71564e30b5c8f40749bf4cfc23 (patch)
tree8f21eee9026c81bebffb240f74609195fe68e96e /src
parentb7dd75c904277630675e432b3398a584d882b5ac (diff)
rustc: Make structured comparison glue instead of emitting it inline
Diffstat (limited to 'src')
-rw-r--r--src/comp/middle/trans.rs529
-rw-r--r--src/comp/middle/ty.rs7
-rw-r--r--src/test/run-pass/seq-compare.rs2
-rw-r--r--src/test/run-pass/structured-compare-recursive.rs9
4 files changed, 350 insertions, 197 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index bb8ecd9c66d..d9b58c114e9 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -374,7 +374,7 @@ fn T_tydesc(type_names tn) -> TypeRef {
                                      T_ptr(T_nil()),
                                      tydescpp,
                                      pvoid), T_void()));
-    auto cmp_glue_fn_ty = T_ptr(T_fn(vec(T_ptr(T_i8()),
+    auto cmp_glue_fn_ty = T_ptr(T_fn(vec(T_ptr(T_i1()),
                                          T_taskptr(tn),
                                          T_ptr(T_nil()),
                                          tydescpp,
@@ -887,6 +887,10 @@ fn C_int(int i) -> ValueRef {
     ret C_integral(i, T_int());
 }
 
+fn C_i8(uint i) -> ValueRef {
+    ret C_integral(i as int, T_i8());
+}
+
 // This is a 'c-like' raw string, which differs from
 // our boxed-and-length-annotated strings.
 fn C_cstr(@crate_ctxt cx, str s) -> ValueRef {
@@ -1730,7 +1734,7 @@ fn make_generic_glue(@local_ctxt cx,
         }
         case (mgghf_cmp) {
             auto llrawptr1 = llvm.LLVMGetParam(llfn, 5u);
-            auto llval1 = bcx.build.BitCast(llrawptr0, llty);
+            auto llval1 = bcx.build.BitCast(llrawptr1, llty);
 
             auto llcmpval = llvm.LLVMGetParam(llfn, 6u);
 
@@ -1980,9 +1984,257 @@ fn decr_refcnt_and_if_zero(@block_ctxt cx,
     ret res(next_cx, phi);
 }
 
-fn make_cmp_glue(@block_ctxt cx, ValueRef v0, ValueRef v1, @ty.t t,
-        ValueRef llop) {
-    cx.build.RetVoid();     // TODO
+// Structural comparison: a rather involved form of glue.
+
+fn make_cmp_glue(@block_ctxt cx,
+                 ValueRef lhs0,
+                 ValueRef rhs0,
+                 @ty.t t,
+                 ValueRef llop) {
+    auto lhs = load_if_immediate(cx, lhs0, t);
+    auto rhs = load_if_immediate(cx, rhs0, t);
+
+    if (ty.type_is_scalar(t)) {
+        make_scalar_cmp_glue(cx, lhs, rhs, t, llop);
+
+    } else if (ty.type_is_box(t)) {
+        lhs = cx.build.GEP(lhs, vec(C_int(0), C_int(abi.box_rc_field_body)));
+        rhs = cx.build.GEP(rhs, vec(C_int(0), C_int(abi.box_rc_field_body)));
+        auto rslt = call_cmp_glue(cx, lhs, rhs, t, llop);
+
+        rslt.bcx.build.Store(rslt.val, cx.fcx.llretptr);
+        rslt.bcx.build.RetVoid();
+
+    } else if (ty.type_is_structural(t)
+               || ty.type_is_sequence(t)) {
+
+        auto scx = new_sub_block_ctxt(cx, "structural compare start");
+        auto next = new_sub_block_ctxt(cx, "structural compare end");
+        cx.build.Br(scx.llbb);
+
+        /*
+         * We're doing lexicographic comparison here. We start with the
+         * assumption that the two input elements are equal. Depending on
+         * operator, this means that the result is either true or false;
+         * equality produces 'true' for ==, <= and >=. It produces 'false' for
+         * !=, < and >.
+         *
+         * We then move one element at a time through the structure checking
+         * for pairwise element equality. If we have equality, our assumption
+         * about overall sequence equality is not modified, so we have to move
+         * to the next element.
+         *
+         * If we do not have pairwise element equality, we have reached an
+         * element that 'decides' the lexicographic comparison. So we exit the
+         * loop with a flag that indicates the true/false sense of that
+         * decision, by testing the element again with the operator we're
+         * interested in.
+         *
+         * When we're lucky, LLVM should be able to fold some of these two
+         * tests together (as they're applied to the same operands and in some
+         * cases are sometimes redundant). But we don't bother trying to
+         * optimize combinations like that, at this level.
+         */
+
+        auto flag = alloca(scx, T_i1());
+        llvm.LLVMSetValueName(flag, _str.buf("flag"));
+
+        auto r;
+        if (ty.type_is_sequence(t)) {
+
+            // If we hit == all the way through the minimum-shared-length
+            // section, default to judging the relative sequence lengths.
+            r = compare_integral_values(scx,
+                                        vec_fill(scx, lhs),
+                                        vec_fill(scx, rhs),
+                                        false,
+                                        llop);
+            r.bcx.build.Store(r.val, flag);
+
+        } else {
+            // == and <= default to true if they find == all the way. <
+            // defaults to false if it finds == all the way.
+            auto result_if_equal = scx.build.ICmp(lib.llvm.LLVMIntNE, llop,
+                                                  C_i8(abi.cmp_glue_op_lt));
+            scx.build.Store(result_if_equal, flag);
+            r = res(scx, C_nil());
+        }
+
+        fn inner(@block_ctxt last_cx,
+                 bool load_inner,
+                 ValueRef flag,
+                 ValueRef llop,
+                 @block_ctxt cx,
+                 ValueRef av0,
+                 ValueRef bv0,
+                 @ty.t t) -> result {
+
+            auto cnt_cx = new_sub_block_ctxt(cx, "continue_comparison");
+            auto stop_cx = new_sub_block_ctxt(cx, "stop_comparison");
+
+            auto av = av0;
+            auto bv = bv0;
+            if (load_inner) {
+                // If `load_inner` is true, then the pointer type will always
+                // be i8, because the data part of a vector always has type
+                // i8[]. So we need to cast it to the proper type.
+
+                if (!ty.type_has_dynamic_size(t)) {
+                    auto llelemty = T_ptr(type_of(last_cx.fcx.lcx.ccx, t));
+                    av = cx.build.PointerCast(av, llelemty);
+                    bv = cx.build.PointerCast(bv, llelemty);
+                }
+
+                av = load_if_immediate(cx, av, t);
+                bv = load_if_immediate(cx, bv, t);
+            }
+
+            // First 'eq' comparison: if so, continue to next elts.
+            auto eq_r = call_cmp_glue(cx, av, bv, t,
+                                      C_i8(abi.cmp_glue_op_eq));
+            eq_r.bcx.build.CondBr(eq_r.val, cnt_cx.llbb, stop_cx.llbb);
+
+            // Second 'op' comparison: find out how this elt-pair decides.
+            auto stop_r = call_cmp_glue(stop_cx, av, bv, t, llop);
+            stop_r.bcx.build.Store(stop_r.val, flag);
+            stop_r.bcx.build.Br(last_cx.llbb);
+            ret res(cnt_cx, C_nil());
+        }
+
+        if (ty.type_is_structural(t)) {
+            r = iter_structural_ty_full(r.bcx, lhs, rhs, t,
+                                        bind inner(next, false, flag, llop,
+                                                   _, _, _, _));
+        } else {
+            auto lhs_p0 = vec_p0(r.bcx, lhs);
+            auto rhs_p0 = vec_p0(r.bcx, rhs);
+            auto min_len = umin(r.bcx, vec_fill(r.bcx, lhs),
+                                vec_fill(r.bcx, rhs));
+            auto rhs_lim = r.bcx.build.GEP(rhs_p0, vec(min_len));
+            auto elt_ty = ty.sequence_element_type(t);
+            r = size_of(r.bcx, elt_ty);
+            r = iter_sequence_raw(r.bcx, lhs_p0, rhs_p0, rhs_lim, r.val,
+                                  bind inner(next, true, flag, llop,
+                                             _, _, _, elt_ty));
+        }
+
+        r.bcx.build.Br(next.llbb);
+        auto v = next.build.Load(flag);
+
+        next.build.Store(v, cx.fcx.llretptr);
+        next.build.RetVoid();
+
+
+    } else {
+        // FIXME: compare obj, fn by pointer?
+        trans_fail(cx, none[common.span],
+                   "attempt to compare values of type " + ty.ty_to_str(t));
+    }
+}
+
+// A helper function to create scalar comparison glue.
+fn make_scalar_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t t,
+                        ValueRef llop) {
+    if (ty.type_is_fp(t)) {
+        make_fp_cmp_glue(cx, lhs, rhs, t, llop);
+        ret;
+    }
+
+    if (ty.type_is_integral(t) || ty.type_is_bool(t)) {
+        make_integral_cmp_glue(cx, lhs, rhs, t, llop);
+        ret;
+    }
+
+    if (ty.type_is_nil(t)) {
+        cx.build.Store(C_bool(true), cx.fcx.llretptr);
+        cx.build.RetVoid();
+        ret;
+    }
+
+    trans_fail(cx, none[common.span],
+               "attempt to compare values of type " + ty.ty_to_str(t));
+}
+
+// A helper function to create floating point comparison glue.
+fn make_fp_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t fptype,
+                    ValueRef llop) {
+    auto last_cx = new_sub_block_ctxt(cx, "last");
+
+    auto eq_cx = new_sub_block_ctxt(cx, "eq");
+    auto eq_result = eq_cx.build.FCmp(lib.llvm.LLVMRealUEQ, lhs, rhs);
+    eq_cx.build.Br(last_cx.llbb);
+
+    auto lt_cx = new_sub_block_ctxt(cx, "lt");
+    auto lt_result = lt_cx.build.FCmp(lib.llvm.LLVMRealULT, lhs, rhs);
+    lt_cx.build.Br(last_cx.llbb);
+
+    auto le_cx = new_sub_block_ctxt(cx, "le");
+    auto le_result = le_cx.build.FCmp(lib.llvm.LLVMRealULE, lhs, rhs);
+    le_cx.build.Br(last_cx.llbb);
+
+    auto unreach_cx = new_sub_block_ctxt(cx, "unreach");
+    unreach_cx.build.Unreachable();
+
+    auto llswitch = cx.build.Switch(llop, unreach_cx.llbb, 3u);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_eq), eq_cx.llbb);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_lt), lt_cx.llbb);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_le), le_cx.llbb);
+
+    auto last_result =
+        last_cx.build.Phi(T_i1(), vec(eq_result, lt_result, le_result),
+                          vec(eq_cx.llbb, lt_cx.llbb, le_cx.llbb));
+    last_cx.build.Store(last_result, cx.fcx.llretptr);
+    last_cx.build.RetVoid();
+}
+
+// A helper function to compare integral values. This is used by both
+// `make_integral_cmp_glue` and `make_cmp_glue`.
+fn compare_integral_values(@block_ctxt cx, ValueRef lhs, ValueRef rhs,
+                           bool signed, ValueRef llop) -> result {
+    auto lt_cmp; auto le_cmp;
+    if (signed) {
+        lt_cmp = lib.llvm.LLVMIntSLT;
+        le_cmp = lib.llvm.LLVMIntSLE;
+    } else {
+        lt_cmp = lib.llvm.LLVMIntULT;
+        le_cmp = lib.llvm.LLVMIntULE;
+    }
+
+    auto last_cx = new_sub_block_ctxt(cx, "last");
+
+    auto eq_cx = new_sub_block_ctxt(cx, "eq");
+    auto eq_result = eq_cx.build.ICmp(lib.llvm.LLVMIntEQ, lhs, rhs);
+    eq_cx.build.Br(last_cx.llbb);
+
+    auto lt_cx = new_sub_block_ctxt(cx, "lt");
+    auto lt_result = lt_cx.build.ICmp(lt_cmp, lhs, rhs);
+    lt_cx.build.Br(last_cx.llbb);
+
+    auto le_cx = new_sub_block_ctxt(cx, "le");
+    auto le_result = le_cx.build.ICmp(le_cmp, lhs, rhs);
+    le_cx.build.Br(last_cx.llbb);
+
+    auto unreach_cx = new_sub_block_ctxt(cx, "unreach");
+    unreach_cx.build.Unreachable();
+
+    auto llswitch = cx.build.Switch(llop, unreach_cx.llbb, 3u);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_eq), eq_cx.llbb);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_lt), lt_cx.llbb);
+    llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_le), le_cx.llbb);
+
+    auto last_result =
+        last_cx.build.Phi(T_i1(), vec(eq_result, lt_result, le_result),
+                          vec(eq_cx.llbb, lt_cx.llbb, le_cx.llbb));
+    ret res(last_cx, last_result);
+}
+
+// A helper function to create integral comparison glue.
+fn make_integral_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs,
+                          @ty.t intype, ValueRef llop) {
+    auto r = compare_integral_values(cx, lhs, rhs, ty.type_is_signed(intype),
+                                     llop);
+    r.bcx.build.Store(r.val, r.bcx.fcx.llretptr);
+    r.bcx.build.RetVoid();
 }
 
 
@@ -2403,6 +2655,42 @@ fn call_tydesc_glue(@block_ctxt cx, ValueRef v, @ty.t t, int field) {
                           td.val, field);
 }
 
+fn call_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t t,
+                 ValueRef llop) -> result {
+    // We can't use call_tydesc_glue_full() and friends here because compare
+    // glue has a special signature.
+
+    auto lllhs = spill_if_immediate(cx, lhs, t);
+    auto llrhs = spill_if_immediate(cx, rhs, t);
+
+    auto llrawlhsptr = cx.build.BitCast(lllhs, T_ptr(T_i8()));
+    auto llrawrhsptr = cx.build.BitCast(llrhs, T_ptr(T_i8()));
+
+    auto r = get_tydesc(cx, t);
+    auto lltydescs =
+        r.bcx.build.GEP(r.val, vec(C_int(0),
+                                   C_int(abi.tydesc_field_first_param)));
+    lltydescs = r.bcx.build.Load(lltydescs);
+    auto llfnptr =
+        r.bcx.build.GEP(r.val, vec(C_int(0),
+                                   C_int(abi.tydesc_field_cmp_glue)));
+    auto llfn = r.bcx.build.Load(llfnptr);
+
+    auto llcmpresultptr = r.bcx.build.Alloca(T_i1());
+
+    let vec[ValueRef] llargs = vec(llcmpresultptr,
+                                   r.bcx.fcx.lltaskptr,
+                                   C_null(T_ptr(T_nil())),
+                                   lltydescs,
+                                   llrawlhsptr,
+                                   llrawrhsptr,
+                                   llop);
+
+    r.bcx.build.FastCall(llfn, llargs);
+
+    ret res(r.bcx, r.bcx.build.Load(llcmpresultptr));
+}
+
 fn take_ty(@block_ctxt cx, ValueRef v, @ty.t t) -> result {
     if (!ty.type_is_scalar(t)) {
         call_tydesc_glue(cx, v, t, abi.tydesc_field_take_glue);
@@ -2661,7 +2949,7 @@ fn trans_unary(@block_ctxt cx, ast.unop op,
 
 fn trans_compare(@block_ctxt cx0, ast.binop op, @ty.t t0,
                  ValueRef lhs0, ValueRef rhs0) -> result {
-
+    // Autoderef both sides.
     auto cx = cx0;
 
     auto lhs_r = autoderef(cx, lhs0, t0);
@@ -2674,194 +2962,30 @@ fn trans_compare(@block_ctxt cx0, ast.binop op, @ty.t t0,
 
     auto t = autoderefed_ty(t0);
 
-    if (ty.type_is_scalar(t)) {
-        ret res(cx, trans_scalar_compare(cx, op, t, lhs, rhs));
-
-    } else if (ty.type_is_structural(t)
-               || ty.type_is_sequence(t)) {
-
-        auto scx = new_sub_block_ctxt(cx, "structural compare start");
-        auto next = new_sub_block_ctxt(cx, "structural compare end");
-        cx.build.Br(scx.llbb);
-
-        /*
-         * We're doing lexicographic comparison here. We start with the
-         * assumption that the two input elements are equal. Depending on
-         * operator, this means that the result is either true or false;
-         * equality produces 'true' for ==, <= and >=. It produces 'false' for
-         * !=, < and >.
-         *
-         * We then move one element at a time through the structure checking
-         * for pairwise element equality. If we have equality, our assumption
-         * about overall sequence equality is not modified, so we have to move
-         * to the next element.
-         *
-         * If we do not have pairwise element equality, we have reached an
-         * element that 'decides' the lexicographic comparison. So we exit the
-         * loop with a flag that indicates the true/false sense of that
-         * decision, by testing the element again with the operator we're
-         * interested in.
-         *
-         * When we're lucky, LLVM should be able to fold some of these two
-         * tests together (as they're applied to the same operands and in some
-         * cases are sometimes redundant). But we don't bother trying to
-         * optimize combinations like that, at this level.
-         */
-
-        auto flag = alloca(scx, T_i1());
-
-        if (ty.type_is_sequence(t)) {
-
-            // If we hit == all the way through the minimum-shared-length
-            // section, default to judging the relative sequence lengths.
-            auto len_cmp =
-                trans_integral_compare(scx, op, plain_ty(ty.ty_uint),
-                                       vec_fill(scx, lhs),
-                                       vec_fill(scx, rhs));
-            scx.build.Store(len_cmp, flag);
-
-        } else {
-            auto T = C_integral(1, T_i1());
-            auto F = C_integral(0, T_i1());
-
-            alt (op) {
-                // ==, <= and >= default to true if they find == all the way.
-                case (ast.eq) { scx.build.Store(T, flag); }
-                case (ast.le) { scx.build.Store(T, flag); }
-                case (ast.ge) { scx.build.Store(T, flag); }
-                case (_) {
-                    // < > default to false if they find == all the way.
-                    scx.build.Store(F, flag);
-                }
-
-            }
-        }
-
-        fn inner(@block_ctxt last_cx,
-                 bool load_inner,
-                 ValueRef flag,
-                 ast.binop op,
-                 @block_ctxt cx,
-                 ValueRef av0,
-                 ValueRef bv0,
-                 @ty.t t) -> result {
-
-            auto cnt_cx = new_sub_block_ctxt(cx, "continue comparison");
-            auto stop_cx = new_sub_block_ctxt(cx, "stop comparison");
-
-            auto av = av0;
-            auto bv = bv0;
-            if (load_inner) {
-                av = load_if_immediate(cx, av, t);
-                bv = load_if_immediate(cx, bv, t);
-            }
-
-            // First 'eq' comparison: if so, continue to next elts.
-            auto eq_r = trans_compare(cx, ast.eq, t, av, bv);
-            eq_r.bcx.build.CondBr(eq_r.val, cnt_cx.llbb, stop_cx.llbb);
-
-            // Second 'op' comparison: find out how this elt-pair decides.
-            auto stop_r = trans_compare(stop_cx, op, t, av, bv);
-            stop_r.bcx.build.Store(stop_r.val, flag);
-            stop_r.bcx.build.Br(last_cx.llbb);
-            ret res(cnt_cx, C_nil());
-        }
-
-        auto r;
-        if (ty.type_is_structural(t)) {
-            r = iter_structural_ty_full(scx, lhs, rhs, t,
-                                        bind inner(next, false, flag, op,
-                                                   _, _, _, _));
-        } else {
-            auto lhs_p0 = vec_p0(scx, lhs);
-            auto rhs_p0 = vec_p0(scx, rhs);
-            auto min_len = umin(scx, vec_fill(scx, lhs), vec_fill(scx, rhs));
-            auto rhs_lim = scx.build.GEP(rhs_p0, vec(min_len));
-            auto elt_ty = ty.sequence_element_type(t);
-            auto elt_llsz_r = size_of(scx, elt_ty);
-            scx = elt_llsz_r.bcx;
-            r = iter_sequence_raw(scx, lhs_p0, rhs_p0, rhs_lim,
-                                  elt_llsz_r.val,
-                                  bind inner(next, true, flag, op,
-                                             _, _, _, elt_ty));
-        }
-
-        r.bcx.build.Br(next.llbb);
-        auto v = next.build.Load(flag);
-        ret res(next, v);
-
-
-    } else {
-        // FIXME: compare obj, fn by pointer?
-        cx.fcx.lcx.ccx.sess.unimpl("type in trans_compare");
-        ret res(cx, C_bool(false));
-    }
-}
-
-fn trans_scalar_compare(@block_ctxt cx, ast.binop op, @ty.t t,
-                        ValueRef lhs, ValueRef rhs) -> ValueRef {
-    if (ty.type_is_fp(t)) {
-        ret trans_fp_compare(cx, op, t, lhs, rhs);
-    } else {
-        ret trans_integral_compare(cx, op, t, lhs, rhs);
-    }
-}
-
-fn trans_fp_compare(@block_ctxt cx, ast.binop op, @ty.t fptype,
-                    ValueRef lhs, ValueRef rhs) -> ValueRef {
-
-    auto cmp = lib.llvm.LLVMIntEQ;
+    // Determine the operation we need.
+    // FIXME: Use or-patterns when we have them.
+    auto llop;
     alt (op) {
-        // FIXME: possibly use the unordered-or-< predicates here,
-        // for now we're only going with ordered-and-< style (no NaNs).
-        case (ast.eq) { cmp = lib.llvm.LLVMRealOEQ; }
-        case (ast.ne) { cmp = lib.llvm.LLVMRealONE; }
-        case (ast.lt) { cmp = lib.llvm.LLVMRealOLT; }
-        case (ast.gt) { cmp = lib.llvm.LLVMRealOGT; }
-        case (ast.le) { cmp = lib.llvm.LLVMRealOLE; }
-        case (ast.ge) { cmp = lib.llvm.LLVMRealOGE; }
+        case (ast.eq) { llop = C_i8(abi.cmp_glue_op_eq); }
+        case (ast.lt) { llop = C_i8(abi.cmp_glue_op_lt); }
+        case (ast.le) { llop = C_i8(abi.cmp_glue_op_le); }
+        case (ast.ne) { llop = C_i8(abi.cmp_glue_op_eq); }
+        case (ast.ge) { llop = C_i8(abi.cmp_glue_op_lt); }
+        case (ast.gt) { llop = C_i8(abi.cmp_glue_op_le); }
     }
 
-    ret cx.build.FCmp(cmp, lhs, rhs);
-}
+    auto rslt = call_cmp_glue(cx, lhs, rhs, t, llop);
 
-fn trans_integral_compare(@block_ctxt cx, ast.binop op, @ty.t intype,
-                          ValueRef lhs, ValueRef rhs) -> ValueRef {
-    auto cmp = lib.llvm.LLVMIntEQ;
+    // Invert the result if necessary.
+    // FIXME: Use or-patterns when we have them.
     alt (op) {
-        case (ast.eq) { cmp = lib.llvm.LLVMIntEQ; }
-        case (ast.ne) { cmp = lib.llvm.LLVMIntNE; }
-
-        case (ast.lt) {
-            if (ty.type_is_signed(intype)) {
-                cmp = lib.llvm.LLVMIntSLT;
-            } else {
-                cmp = lib.llvm.LLVMIntULT;
-            }
-        }
-        case (ast.le) {
-            if (ty.type_is_signed(intype)) {
-                cmp = lib.llvm.LLVMIntSLE;
-            } else {
-                cmp = lib.llvm.LLVMIntULE;
-            }
-        }
-        case (ast.gt) {
-            if (ty.type_is_signed(intype)) {
-                cmp = lib.llvm.LLVMIntSGT;
-            } else {
-                cmp = lib.llvm.LLVMIntUGT;
-            }
-        }
-        case (ast.ge) {
-            if (ty.type_is_signed(intype)) {
-                cmp = lib.llvm.LLVMIntSGE;
-            } else {
-                cmp = lib.llvm.LLVMIntUGE;
-            }
-        }
+        case (ast.eq) { ret res(rslt.bcx, rslt.val);                     }
+        case (ast.lt) { ret res(rslt.bcx, rslt.val);                     }
+        case (ast.le) { ret res(rslt.bcx, rslt.val);                     }
+        case (ast.ne) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
+        case (ast.ge) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
+        case (ast.gt) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
     }
-    ret cx.build.ICmp(cmp, lhs, rhs);
 }
 
 fn trans_vec_append(@block_ctxt cx, @ty.t t,
@@ -3678,7 +3802,7 @@ fn trans_alt(@block_ctxt cx, @ast.expr expr,
     }
 
     auto default_cx = this_cx;
-    auto default_res = trans_fail(default_cx, expr.span,
+    auto default_res = trans_fail(default_cx, some[common.span](expr.span),
                                   "non-exhaustive match failure");
 
     // FIXME: This isn't quite right, particularly re: dynamic types
@@ -3976,7 +4100,8 @@ fn trans_index(@block_ctxt cx, &ast.span sp, @ast.expr base,
     bcx.build.CondBr(bounds_check, next_cx.llbb, fail_cx.llbb);
 
     // fail: bad bounds check.
-    auto fail_res = trans_fail(fail_cx, sp, "bounds check");
+    auto fail_res = trans_fail(fail_cx, some[common.span](sp),
+                               "bounds check");
 
     auto body = next_cx.build.GEP(v, vec(C_int(0), C_int(abi.vec_elt_data)));
     auto elt;
@@ -4893,7 +5018,7 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result {
         }
 
         case (ast.expr_fail(_)) {
-            ret trans_fail(cx, e.span, "explicit failure");
+            ret trans_fail(cx, some[common.span](e.span), "explicit failure");
         }
 
         case (ast.expr_log(?lvl, ?a, _)) {
@@ -5062,7 +5187,7 @@ fn trans_check_expr(@block_ctxt cx, @ast.expr e) -> result {
 
     auto expr_str = pretty.pprust.expr_to_str(e);
     auto fail_cx = new_sub_block_ctxt(cx, "fail");
-    auto fail_res = trans_fail(fail_cx, e.span, expr_str);
+    auto fail_res = trans_fail(fail_cx, some[common.span](e.span), expr_str);
 
     auto next_cx = new_sub_block_ctxt(cx, "next");
     cond_res.bcx.build.CondBr(cond_res.val,
@@ -5071,11 +5196,23 @@ fn trans_check_expr(@block_ctxt cx, @ast.expr e) -> result {
     ret res(next_cx, C_nil());
 }
 
-fn trans_fail(@block_ctxt cx, common.span sp, str fail_str) -> result {
+fn trans_fail(@block_ctxt cx, option.t[common.span] sp_opt, str fail_str)
+        -> result {
     auto V_fail_str = p2i(C_cstr(cx.fcx.lcx.ccx, fail_str));
-    auto loc = cx.fcx.lcx.ccx.sess.lookup_pos(sp.lo);
-    auto V_filename = p2i(C_cstr(cx.fcx.lcx.ccx, loc.filename));
-    auto V_line = loc.line as int;
+
+    auto V_filename; auto V_line;
+    alt (sp_opt) {
+        case (some[common.span](?sp)) {
+            auto loc = cx.fcx.lcx.ccx.sess.lookup_pos(sp.lo);
+            V_filename = p2i(C_cstr(cx.fcx.lcx.ccx, loc.filename));
+            V_line = loc.line as int;
+        }
+        case (none[common.span]) {
+            V_filename = p2i(C_str(cx.fcx.lcx.ccx, "<runtime>"));
+            V_line = 0;
+        }
+    }
+
     auto args = vec(V_fail_str, V_filename, C_int(V_line));
 
     auto sub = trans_upcall(cx, "upcall_fail", args);
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 06fbf76ef3c..a25c00576c1 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -437,6 +437,13 @@ fn type_is_nil(@t ty) -> bool {
     fail;
 }
 
+fn type_is_bool(@t ty) -> bool {
+    alt (ty.struct) {
+        case (ty_bool) { ret true; }
+        case (_) { ret false; }
+    }
+}
+
 
 fn type_is_structural(@t ty) -> bool {
     alt (ty.struct) {
diff --git a/src/test/run-pass/seq-compare.rs b/src/test/run-pass/seq-compare.rs
index 36db2b4667e..e8bc4788c9a 100644
--- a/src/test/run-pass/seq-compare.rs
+++ b/src/test/run-pass/seq-compare.rs
@@ -13,4 +13,4 @@ fn main() {
   check (vec(1,2,3,4) > vec(1,2,3));
   check (vec(1,2,3) == vec(1,2,3));
   check (vec(1,2,3) != vec(1,1,3));
-}
\ No newline at end of file
+}
diff --git a/src/test/run-pass/structured-compare-recursive.rs b/src/test/run-pass/structured-compare-recursive.rs
new file mode 100644
index 00000000000..3049faec6b6
--- /dev/null
+++ b/src/test/run-pass/structured-compare-recursive.rs
@@ -0,0 +1,9 @@
+tag taggy {
+    foo(@taggy);
+    bar;
+}
+
+fn main() {
+    check (bar <= bar);
+}
+