/*
 * $Id: cmp.ops 47428 2010-06-06 08:32:34Z cotto $
** cmp.ops
*/

=head1 NAME

cmp.ops - Comparison Opcodes

=cut

=head1 DESCRIPTION

Operations that compare two registers against each other.
Some of these operations affect control flow directly; others
do not.

When making changes to any ops file, run C<make bootstrap-ops> to regenerate
all generated ops files.

=cut

###############################################################################

=head2 Flow control comparison operators

These operators branch based on the relationship between
their operands.

=over 4

=cut

########################################

=item B<eq>(in INT, in INT, inconst LABEL)

=item B<eq>(in NUM, in NUM, inconst LABEL)

=item B<eq>(in STR, in STR, inconst LABEL)

=item B<eq>(invar PMC, invar PMC, inconst LABEL)

=item B<eq>(invar PMC, in INT, inconst LABEL)

=item B<eq>(invar PMC, in NUM, inconst LABEL)

=item B<eq>(invar PMC, in STR, inconst LABEL)

=item B<eq_str>(invar PMC, invar PMC, inconst LABEL)

=item B<eq_num>(invar PMC, invar PMC, inconst LABEL)

=item B<eq_addr>(in STR, in STR, inconst LABEL)

=item B<eq_addr>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is equal to $2. For PMCs this does a generic comparison,
whatever that is for the involved PMCs. Mixed PMC number/string
operands do a numeric or string comparison respectively.

B<eq_str> and B<eq_num> enforce string or number comparisons.

B<eq_addr> compares the address of the objects or strings.

=cut

inline op eq(in INT, in INT, inconst LABEL) :base_core {
    if ($1 == $2) {
        goto OFFSET($3);
    }
}

inline op eq(in NUM, in NUM, inconst LABEL) :base_core {
    if ($1 == $2) {
        goto OFFSET($3);
    }
}

op eq(in STR, in STR, inconst LABEL) :base_core {
    if (Parrot_str_equal(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op eq(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_is_equal(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op eq(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (VTABLE_is_equal(interp, $1, temp)) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op eq(invar PMC, in NUM, inconst LABEL) :base_core {
    /*
     * the get_number and get_string should probably
     * be also replaced with code like above, as
     * overriding the compare multi subs wouldn't
     * have any effect with the current code
     */
    if (VTABLE_get_number(interp, $1) == $2) {
        goto OFFSET($3);
    }
}

op eq(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_equal(interp, VTABLE_get_string(interp, $1), $2)) {
        goto OFFSET($3);
    }
}

op eq_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_is_equal_string(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op eq_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_is_equal_num(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op eq_addr(in STR, in STR, inconst LABEL) :base_core {
    if ($1 == $2) {
        goto OFFSET($3);
    }
}

op eq_addr(invar PMC, invar PMC, inconst LABEL) :base_core {
    if ($1 == $2) {
        goto OFFSET($3);
    }
}

########################################

=item B<ne>(in INT, in INT, inconst LABEL)

=item B<ne>(in NUM, in NUM, inconst LABEL)

=item B<ne>(in STR, in STR, inconst LABEL)

=item B<ne>(invar PMC, invar PMC, inconst LABEL)

=item B<ne>(invar PMC, in INT, inconst LABEL)

=item B<ne>(invar PMC, in NUM, inconst LABEL)

=item B<ne>(invar PMC, in STR, inconst LABEL)

=item B<ne_str>(invar PMC, invar PMC, inconst LABEL)

=item B<ne_num>(invar PMC, invar PMC, inconst LABEL)

=item B<ne_addr>(in STR, in STR, inconst LABEL)

=item B<ne_addr>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is not equal to $2.

=cut

inline op ne(in INT, in INT, inconst LABEL) :base_core {
    if ($1 != $2) {
        goto OFFSET($3);
    }
}

inline op ne(in NUM, in NUM, inconst LABEL) :base_core {
    if ($1 != $2) {
        goto OFFSET($3);
    }
}

op ne(in STR, in STR, inconst LABEL) :base_core {
    if (Parrot_str_not_equal(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op ne(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (!VTABLE_is_equal(interp, $1, $2)) {
        goto OFFSET($3);
    }
}

op ne(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (!VTABLE_is_equal(interp, $1, temp)) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op ne(invar PMC, in NUM, inconst LABEL) :base_core {
    if (VTABLE_get_number(interp, $1) != $2) {
        goto OFFSET($3);
    }
}

op ne(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_not_equal(interp, VTABLE_get_string(interp, $1), $2)) {
        goto OFFSET($3);
    }
}

op ne_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_string(interp, $1, $2) != 0) {
        goto OFFSET($3);
    }
}

op ne_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_num(interp, $1, $2) != 0) {
        goto OFFSET($3);
    }
}

op ne_addr(in STR, in STR, inconst LABEL) :base_core {
    if ($1 != $2) {
        goto OFFSET($3);
    }
}

op ne_addr(invar PMC, invar PMC, inconst LABEL) :base_core {
    if ($1 != $2) {
        goto OFFSET($3);
    }
}

########################################

=item B<lt>(in INT, in INT, inconst LABEL)

=item B<lt>(in NUM, in NUM, inconst LABEL)

=item B<lt>(in STR, in STR, inconst LABEL)

=item B<lt>(invar PMC, invar PMC, inconst LABEL)

=item B<lt>(invar PMC, in INT, inconst LABEL)

=item B<lt>(invar PMC, in NUM, inconst LABEL)

=item B<lt>(invar PMC, in STR, inconst LABEL)

=item B<lt_str>(invar PMC, invar PMC, inconst LABEL)

=item B<lt_num>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is less than $2.

=cut

inline op lt(in INT, in INT, inconst LABEL) :base_core {
    if ($1 < $2) {
        goto OFFSET($3);
    }
}

inline op lt(in NUM, in NUM, inconst LABEL) :base_core {
    if ($1 < $2) {
        goto OFFSET($3);
    }
}

op lt(in STR, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, $1, $2) < 0) {
        goto OFFSET($3);
    }
}

op lt(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp(interp, $1, $2) < 0) {
        goto OFFSET($3);
    }
}

op lt(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (VTABLE_cmp(interp, $1, temp) < 0) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op lt(invar PMC, in NUM, inconst LABEL) :base_core {
    if (VTABLE_get_number(interp, $1) < $2) {
        goto OFFSET($3);
    }
}

op lt(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, VTABLE_get_string(interp, $1), $2) < 0) {
        goto OFFSET($3);
    }
}

op lt_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_string(interp, $1, $2) < 0) {
        goto OFFSET($3);
    }
}

op lt_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_num(interp, $1, $2) < 0) {
        goto OFFSET($3);
    }
}

########################################

=item B<le>(in INT, in INT, inconst LABEL)

=item B<le>(in NUM, in NUM, inconst LABEL)

=item B<le>(in STR, in STR, inconst LABEL)

=item B<le>(invar PMC, invar PMC, inconst LABEL)

=item B<le>(invar PMC, in INT, inconst LABEL)

=item B<le>(invar PMC, in NUM, inconst LABEL)

=item B<le>(invar PMC, in STR, inconst LABEL)

=item B<le_str>(invar PMC, invar PMC, inconst LABEL)

=item B<le_num>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is less than or equal to $2.

=cut

inline op le(in INT, in INT, inconst LABEL) :base_core {
    if ($1 <= $2) {
        goto OFFSET($3);
    }
}

inline op le(in NUM, in NUM, inconst LABEL) :base_core {
    if ($1 <= $2) {
        goto OFFSET($3);
    }
}

op le(in STR, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, $1, $2) <= 0) {
        goto OFFSET($3);
    }
}

op le(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp(interp, $1, $2) <= 0) {
        goto OFFSET($3);
    }
}

op le(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (VTABLE_cmp(interp, $1, temp) <= 0) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op le(invar PMC, in NUM, inconst LABEL) :base_core {
    if (VTABLE_get_number(interp, $1) <= $2) {
        goto OFFSET($3);
    }
}

op le(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, VTABLE_get_string(interp, $1), $2) <= 0) {
        goto OFFSET($3);
    }
}

op le_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_string(interp, $1, $2) <= 0) {
        goto OFFSET($3);
    }
}

op le_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_num(interp, $1, $2) <= 0) {
        goto OFFSET($3);
    }
}

########################################

=item B<gt>(invar PMC, invar PMC, inconst LABEL)

=item B<gt>(invar PMC, in INT, inconst LABEL)

=item B<gt>(invar PMC, in NUM, inconst LABEL)

=item B<gt>(invar PMC, in STR, inconst LABEL)

=item B<gt_str>(invar PMC, invar PMC, inconst LABEL)

=item B<gt_num>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is greater than $2.

=cut

op gt(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp(interp, $1, $2) > 0) {
        goto OFFSET($3);
    }
}

op gt(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (VTABLE_cmp(interp, $1, temp) > 0) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op gt(invar PMC, in NUM, inconst LABEL) :base_core {
    if (VTABLE_get_number(interp, $1) > $2) {
        goto OFFSET($3);
    }
}

op gt(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, VTABLE_get_string(interp, $1), $2) > 0) {
        goto OFFSET($3);
    }
}

op gt_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_string(interp, $1, $2) > 0) {
        goto OFFSET($3);
    }
}

op gt_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_num(interp, $1, $2) > 0) {
        goto OFFSET($3);
    }
}

########################################

=item B<ge>(invar PMC, invar PMC, inconst LABEL)

=item B<ge>(invar PMC, in INT, inconst LABEL)

=item B<ge>(invar PMC, in NUM, inconst LABEL)

=item B<ge>(invar PMC, in STR, inconst LABEL)

=item B<ge_str>(invar PMC, invar PMC, inconst LABEL)

=item B<ge_num>(invar PMC, invar PMC, inconst LABEL)

Branch if $1 is greater than or equal to $2.

=cut

op ge(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp(interp, $1, $2) >= 0) {
        goto OFFSET($3);
    }
}

op ge(invar PMC, in INT, inconst LABEL) :base_core {
    PMC * const temp = Parrot_pmc_new_temporary(interp, enum_class_Integer);
    VTABLE_set_integer_native(interp, temp, $2);

    if (VTABLE_cmp(interp, $1, temp) >= 0) {
        Parrot_pmc_free_temporary(interp, temp);
        goto OFFSET($3);
    }

    Parrot_pmc_free_temporary(interp, temp);
}

op ge(invar PMC, in NUM, inconst LABEL) :base_core {
    if (VTABLE_get_number(interp, $1) >= $2) {
        goto OFFSET($3);
    }
}

op ge(invar PMC, in STR, inconst LABEL) :base_core {
    if (Parrot_str_compare(interp, VTABLE_get_string(interp, $1), $2) >= 0) {
        goto OFFSET($3);
    }
}

op ge_str(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_string(interp, $1, $2) >= 0) {
        goto OFFSET($3);
    }
}

op ge_num(invar PMC, invar PMC, inconst LABEL) :base_core {
    if (VTABLE_cmp_num(interp, $1, $2) >= 0) {
        goto OFFSET($3);
    }
}

########################################

=item B<if_null>(invar PMC, inconst LABEL)

Branch to $2 if $1 is a NULL PMC.

=item B<if_null>(invar STR, inconst LABEL)

Branch to $2 if $1 is a NULL STRING.

=cut

op if_null(invar PMC, inconst LABEL) {
    if (PMC_IS_NULL($1)) {
        goto OFFSET($2);
    }
}

op if_null(invar STR, inconst LABEL) {
    if (STRING_IS_NULL($1)) {
        goto OFFSET($2);
    }
}

########################################

=item B<unless_null>(invar PMC, inconst LABEL)

Branch to $2 if $1 is I<not> a NULL PMC.

=item B<unless_null>(invar STR, inconst LABEL)

Branch to $2 if $1 is I<not> a NULL STRING.

=cut

op unless_null(invar PMC, inconst LABEL) {
    if (!PMC_IS_NULL($1)) {
        goto OFFSET($2);
    }
}

op unless_null(invar STR, inconst LABEL) {
    if (!STRING_IS_NULL($1)) {
        goto OFFSET($2);
    }
}

=back

=cut


###############################################################################

=head2 Non-flow control comparison ops

These ops do comparisons and truth testing without doing flow control.

=over 4

=item B<cmp>(out INT, in INT, in INT)

=item B<cmp>(out INT, in NUM, in NUM)

=item B<cmp>(out INT, in STR, in STR)

=item B<cmp>(out INT, invar PMC, invar PMC)

=item B<cmp>(out INT, invar PMC, in INT)

=item B<cmp>(out INT, invar PMC, in NUM)

=item B<cmp>(out INT, invar PMC, in STR)

=item B<cmp_str>(out INT, invar PMC, invar PMC)

=item B<cmp_num>(out INT, invar PMC, invar PMC)

Sets $1 to -1 if $2 < $3, +1 if $2 > $3, and 0 otherwise.

=cut

inline op cmp(out INT, in INT, in INT) :base_core {
    $1 = $2 < $3 ? -1 :
         $2 > $3 ? +1 :
         0;
}

inline op cmp(out INT, in NUM, in NUM) :base_core {
    $1 = $2 < $3 ? -1 :
         $2 > $3 ? +1 :
         0;
}

inline op cmp(out INT, in STR, in STR) :base_core {
    $1 = Parrot_str_compare(interp, $2, $3);
}

inline op cmp(out INT, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_cmp(interp, $2, $3);
}

inline op cmp(out INT, invar PMC, in INT) :base_core {
    const INTVAL l = VTABLE_get_integer(interp, $2);
    $1 = l < $3 ? -1 :
         l > $3 ? +1 :
         0;
}

inline op cmp(out INT, invar PMC, in NUM) :base_core {
    const FLOATVAL l = VTABLE_get_number(interp, $2);
    $1 = l < $3 ? -1 :
         l > $3 ? +1 :
         0;
}

inline op cmp(out INT, invar PMC, in STR) :base_core {
    STRING* const l = VTABLE_get_string(interp, $2);
    $1 = Parrot_str_compare(interp, l, $3);
}

inline op cmp_str(out INT, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_cmp_string(interp, $2, $3);
}

inline op cmp_num(out INT, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_cmp_num(interp, $2, $3);
}

=item B<cmp_pmc>(out PMC, invar PMC, invar PMC)

Does the same thing as the other cmp_* variants, but puts a PMC in $1.  This is
useful for avoiding the semipredicate problem in HLLs where two aggregate PMCs
are not always comparable.

=cut

inline op cmp_pmc(out PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_cmp_pmc(interp, $2, $3);
}

########################################

=item B<issame>(out INT, invar PMC, invar PMC)

=item B<issame>(out INT, in STR, in STR)

Sets $1 to 1 or 0, depending on the identity of the 2 objects.
The identity is the PMCs memory address.

=item B<isntsame>(out INT, invar PMC, invar PMC)

=item B<isntsame>(out INT, in STR, in STR)

Sets $1 to 0 or 1, depending on the identity of the 2 objects.

=item B<istrue>(out INT, invar PMC)

Sets $1 to 1 or 0, depending on whether $2 is true or not.

=item B<isfalse>(out INT, invar PMC)

Sets $1 to 1 or 0, depending on whether $2 is false or not.

=item B<isnull>(out INT, in PMC)

Sets $1 to 1 if the object is null, 0 otherwise.

=item B<isnull>(out INT, in STR)

Sets $1 to 1 if the string is null, 0 otherwise.

=cut

inline op issame(out INT, invar PMC, invar PMC) {
    if ($2 == $3)
        $1 = 1;
    else
        $1 = VTABLE_is_same(interp, $2, $3);
}

inline op issame(out INT, in STR, in STR) {
    $1 = $2 == $3;
}

inline op isntsame(out INT, invar PMC, invar PMC) {
    if ($2 == $3)
        $1 = 0;
    else
        $1 = !VTABLE_is_same(interp, $2, $3);
}

inline op isntsame(out INT, in STR, in STR) {
    $1 = $2 != $3;
}

inline op istrue(out INT, invar PMC) {
    $1 = VTABLE_get_bool(interp, $2);
}

inline op isfalse(out INT, invar PMC) {
    $1 = !VTABLE_get_bool(interp, $2);
}

inline op isnull(out INT, in PMC) {
    $1 = PMC_IS_NULL($2);
}

inline op isnull(out INT, in STR) {
    $1 = STRING_IS_NULL($2);
}

=item B<isgt>(out INT, in INT, in INT)

=item B<isgt>(out INT, in NUM, in NUM)

=item B<isgt>(out INT, in STR, in STR)

These 3 opcodes are valid, but create as their reverse.

=item B<isgt>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is greater than $3.

=cut

inline op isgt(out INT, invar PMC, invar PMC) {
    $1 = (VTABLE_cmp(interp, $2, $3) > 0);
}

=item B<isge>(out INT, in INT, in INT)

=item B<isge>(out INT, in NUM, in NUM)

=item B<isge>(out INT, in STR, in STR)

These 3 opcodes are valid, but create as their reverse.

=item B<isge>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is greater than or equal to $3.

=cut

inline op isge(out INT, invar PMC, invar PMC) {
    $1 = (VTABLE_cmp(interp, $2, $3) >= 0);
}

=item B<isle>(out INT, in INT, in INT)

=item B<isle>(out INT, in NUM, in NUM)

=item B<isle>(out INT, in STR, in STR)

=item B<isle>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is less than or equal to $3.

=cut

inline op isle(out INT, in INT, in INT) {
    $1 = $2 <= $3;
}

inline op isle(out INT, in NUM, in NUM) {
    $1 = $2 <= $3;
}

inline op isle(out INT, in STR, in STR) {
    $1 = Parrot_str_compare(interp, $2, $3) <= 0;
}

inline op isle(out INT, invar PMC, invar PMC) {
    $1 = (VTABLE_cmp(interp, $2, $3) <= 0);
}

=item B<islt>(out INT, in INT, in INT)

=item B<islt>(out INT, in NUM, in NUM)

=item B<islt>(out INT, in STR, in STR)

=item B<islt>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is less than $3.

=cut

inline op islt(out INT, in INT, in INT) {
    $1 = ($2 < $3) ? 1 : 0;
}

inline op islt(out INT, in NUM, in NUM) {
    $1 = $2 < $3;
}

inline op islt(out INT, in STR, in STR) {
    $1 = Parrot_str_compare(interp, $2, $3) < 0;
}

inline op islt(out INT, invar PMC, invar PMC) {
    $1 = (VTABLE_cmp(interp, $2, $3) < 0);
}

=item B<iseq>(out INT, in INT, in INT)

=item B<iseq>(out INT, in NUM, in NUM)

=item B<iseq>(out INT, in STR, in STR)

=item B<iseq>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is equal to $3.

=cut

inline op iseq(out INT, in INT, in INT) {
    $1 = ($2 == $3) ? 1 : 0;
}

inline op iseq(out INT, in NUM, in NUM) {
    $1 = $2 == $3;
}

inline op iseq(out INT, in STR, in STR) {
    $1 = (Parrot_str_equal(interp, $2, $3) ? 1 : 0);
}

inline op iseq(out INT, invar PMC, invar PMC) {
    if (&$2 == &$3)
        $1 = 1;
    else
        $1 = VTABLE_is_equal(interp, $2, $3);
}

=item B<isne>(out INT, in INT, in INT)

=item B<isne>(out INT, in NUM, in NUM)

=item B<isne>(out INT, in STR, in STR)

=item B<isne>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is not equal to $3.

=cut

inline op isne(out INT, in INT, in INT) {
    $1 = ($2 == $3) ? 0 : 1;
}

inline op isne(out INT, in NUM, in NUM) {
    $1 = $2 != $3;
}

inline op isne(out INT, in STR, in STR) {
    $1 = (Parrot_str_not_equal(interp, $2, $3) ? 1 : 0);
}

inline op isne(out INT, invar PMC, invar PMC) {
    if (&$2 == &$3)
        $1 = 0;
    else
        $1 = !VTABLE_is_equal(interp, $2, $3);
}

=back

=cut


###############################################################################

=head2 Logical operations

These operations apply logical functions to their arguments.

=over 4

=cut

########################################

=item B<and>(out INT, in INT, in INT)

=item B<and>(invar PMC, invar PMC, invar PMC)

Short-circuiting logical and.  Returns $2 if it's false, else returns $3.

=cut

inline op and(out INT, in INT, in INT) :base_core {
    $1 = $2 ? $3 : $2;
}

inline op and(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_logical_and(interp, $2, $3, $1);
}

########################################

=item B<not>(inout INT)

=item B<not>(invar PMC)

Set the boolean state of $1 to the opposite of the boolean state from $1.

=item B<not>(out INT, in INT)

=item B<not>(out PMC, invar PMC)

=cut

inline op not(inout INT) :base_core {
    $1 = ! $1;
}

inline op not(out INT, in INT) :base_core {
    $1 = ! $2;
}

inline op not(invar PMC) :base_core {
    VTABLE_i_logical_not(interp, $1);
}

inline op not(out PMC, invar PMC) :base_core {
    $1 = VTABLE_logical_not(interp, $2, $1);
}

########################################

=item B<or>(out INT, in INT, in INT)

=item B<or>(invar PMC, invar PMC, invar PMC)

Short-circuiting logical or.  Returns $2 if it's true, else returns $3.

=cut

inline op or(out INT, in INT, in INT) :base_core {
    $1 = $2 ? $2 : $3;
}

inline op or(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_logical_or(interp, $2, $3, $1);
}

########################################

=item B<xor>(out INT, in INT, in INT)

=item B<xor>(invar PMC, invar PMC, invar PMC)

Logical xor.  Returns $2 if it's true and $3 isn't,
returns $3 if it's true and $2 isn't, else returns false.

=cut

inline op xor(out INT, in INT, in INT) :base_core {
    $1 = ($2 && ! $3) ? $2 : ($3 && ! $2) ? $3 : 0;
}

inline op xor(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_logical_xor(interp, $2, $3, $1);
}

=back

=cut


###############################################################################

=head1 COPYRIGHT

Copyright (C) 2001-2010, Parrot Foundation.

=head1 LICENSE

This program is free software. It is subject to the same license
as the Parrot interpreter itself.

=cut

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
