blob: dd7ebace619a198d1109092da935e5ba70c0c423 [file] [log] [blame]
//
// unop vA, vB
// Format 12x: B|A|op
// (see floating_point.S for float/double ops)
//
// neg-int vA, vB
// Format 12x: B|A|7b
%def op_neg_int():
% generic_unop(instr="negw t1, t1")
// not-int vA, vB
// Format 12x: B|A|7c
%def op_not_int():
% generic_unop(instr="not t1, t1")
// neg-long vA, vB
// Format 12x: B|A|7d
%def op_neg_long():
% generic_unop(instr="neg t1, t1", is_wide=True)
// not-long vA, vB
// Format 12x: B|A|7e
%def op_not_long():
% generic_unop(instr="not t1, t1", is_wide=True)
// int-to-long vA, vB
// Format 12x: B|A|81
// Note: Sign extension of int32 into int64.
// Read from 32-bit vreg and write to 64-bit vreg, hence a custom impl.
%def op_int_to_long():
srliw t1, xINST, 12 // t1 := B
srliw t2, xINST, 8 // t2 := B|A
% get_vreg("t1", "t1") # t1 := fp[B] with sign extension to 64 bits
FETCH_ADVANCE_INST 1 // advance xPC, load xINST
and t2, t2, 0xF // t2 := A
GET_INST_OPCODE t3 // t3 holds next opcode
SET_VREG_WIDE t1, t2, z0=t0
// fp[A:A+1] := t1
GOTO_OPCODE t3 // continue to next
// long-to-int vA, vB
// Format 12x: B|A|84
// Note: Truncation of int64 into int32.
// Implemented as a read from the low bits from vB, write them to vA.
// Note: instr is intentionally empty.
%def op_long_to_int():
% generic_unop(instr="")
// int-to-byte vA, vB
// Format 12x: B|A|8d
// Note: Truncation of int32 to int8, sign extending the result.
%def op_int_to_byte():
% generic_unop(instr="sext.b t1, t1")
// int-to-byte vA, vB
// Format 12x: B|A|8e
// Note: Truncation of int32 to uint16, without sign extension.
%def op_int_to_char():
% generic_unop(instr="zext.h t1, t1")
// int-to-byte vA, vB
// Format 12x: B|A|8f
// Note: Truncation of int32 to int16, sign extending the result.
%def op_int_to_short():
% generic_unop(instr="sext.h t1, t1")
// unop boilerplate
// instr: operand held in t1, result written to t1.
// instr must not clobber t2.
// Clobbers: t0, t1, t2
%def generic_unop(instr, is_wide=False):
srliw t1, xINST, 12 // t1 := B
srliw t2, xINST, 8 // t2 := B|A
% get_vreg("t1", "t1", is_wide=is_wide)
// t1 := fp[B]
and t2, t2, 0xF // t2 := A
FETCH_ADVANCE_INST 1 // advance xPC, load xINST
$instr // read t1, write result to t1.
// do not clobber t2!
% set_vreg("t1", "t2", z0="t0", is_wide=is_wide)
// fp[A] := t1
GET_INST_OPCODE t0 // t0 holds next opcode
GOTO_OPCODE t0 // continue to next
//
// binop vAA, vBB, vCC
// Format 23x: AA|op CC|BB
// (see floating_point.S for float/double ops)
//
// add-int vAA, vBB, vCC
// Format 23x: AA|90 CC|BB
%def op_add_int():
% generic_binop(instr="addw t1, t1, t2")
// sub-int vAA, vBB, vCC
// Format 23x: AA|91 CC|BB
%def op_sub_int():
% generic_binop(instr="subw t1, t1, t2")
// mul-int vAA, vBB, vCC
// Format 23x: AA|92 CC|BB
%def op_mul_int():
% generic_binop(instr="mulw t1, t1, t2")
// div-int vAA, vBB, vCC
// Format 23x: AA|93 CC|BB
// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
// ArithmeticException if b == 0.
%def op_div_int():
% generic_binop(instr="divw t1, t1, t2", divz_throw=True)
// rem-int vAA, vBB, vCC
// Format 23x: AA|94 CC|BB
// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
// b == 0.
%def op_rem_int():
% generic_binop(instr="remw t1, t1, t2", divz_throw=True)
// and-int vAA, vBB, vCC
// Format 23x: AA|95 CC|BB
%def op_and_int():
% generic_binop(instr="and t1, t1, t2")
// or-int vAA, vBB, vCC
// Format 23x: AA|96 CC|BB
%def op_or_int():
% generic_binop(instr="or t1, t1, t2")
// xor-int vAA, vBB, vCC
// Format 23x: AA|97 CC|BB
%def op_xor_int():
% generic_binop(instr="xor t1, t1, t2")
// shl-int vAA, vBB, vCC
// Format 23x: AA|98 CC|BB
// Note: SLLW uses t2[4:0] for the shift amount.
%def op_shl_int():
% generic_binop(instr="sllw t1, t1, t2")
// shr-int vAA, vBB, vCC
// Format 23x: AA|99 CC|BB
// Note: SRAW uses t2[4:0] for the shift amount.
%def op_shr_int():
% generic_binop(instr="sraw t1, t1, t2")
// ushr-int vAA, vBB, vCC
// Format 23x: AA|9a CC|BB
// Note: SRLW uses t2[4:0] for the shift amount.
%def op_ushr_int():
% generic_binop(instr="srlw t1, t1, t2")
// add-long vAA, vBB, vCC
// Format 23x: AA|9b CC|BB
%def op_add_long():
% generic_binop(instr="add t1, t1, t2", is_wide=True)
// sub-long vAA, vBB, vCC
// Format 23x: AA|9c CC|BB
%def op_sub_long():
% generic_binop(instr="sub t1, t1, t2", is_wide=True)
// mul-long vAA, vBB, vCC
// Format 23x: AA|9d CC|BB
%def op_mul_long():
% generic_binop(instr="mul t1, t1, t2", is_wide=True)
// div-long vAA, vBB, vCC
// Format 23x: AA|9e CC|BB
// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
// ArithmeticException if b == 0.
%def op_div_long():
% generic_binop(instr="div t1, t1, t2", divz_throw=True, is_wide=True)
// rem-long vAA, vBB, vCC
// Format 23x: AA|9f CC|BB
// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
// b == 0.
%def op_rem_long():
% generic_binop(instr="rem t1, t1, t2", divz_throw=True, is_wide=True)
// and-long vAA, vBB, vCC
// Format 23x: AA|a0 CC|BB
%def op_and_long():
% generic_binop(instr="and t1, t1, t2", is_wide=True)
// or-long vAA, vBB, vCC
// Format 23x: AA|a1 CC|BB
%def op_or_long():
% generic_binop(instr="or t1, t1, t2", is_wide=True)
// xor-long vAA, vBB, vCC
// Format 23x: AA|a2 CC|BB
%def op_xor_long():
% generic_binop(instr="xor t1, t1, t2", is_wide=True)
// shl-long vAA, vBB, vCC
// Format 23x: AA|a3 CC|BB
// Note: SLL uses t2[5:0] for the shift amount.
%def op_shl_long():
% generic_shift_wide(instr="sll t1, t1, t2")
// shr-long vAA, vBB, vCC
// Format 23x: AA|a4 CC|BB
// Note: SRA uses t2[5:0] for the shift amount.
%def op_shr_long():
% generic_shift_wide(instr="sra t1, t1, t2")
// ushr-long vAA, vBB, vCC
// Format 23x: AA|a5 CC|BB
// Note: SRL uses t2[5:0] for the shift amount.
%def op_ushr_long():
% generic_shift_wide(instr="srl t1, t1, t2")
// binop boilerplate
// instr: operands held in t1 and t2, result written to t1.
// instr must not throw. Exceptions to be thrown prior to instr.
// instr must not clobber t3.
//
// The divz_throw flag generates check-and-throw code for div/0.
// The is_wide flag ensures vregs are read and written in 64-bit widths.
// Clobbers: t0, t1, t2, t3
%def generic_binop(instr, divz_throw=False, is_wide=False):
FETCH t1, count=1 // t1 := CC|BB
srliw t3, xINST, 8 // t3 := AA
srliw t2, t1, 8 // t2 := CC
and t1, t1, 0xFF // t1 := BB
% get_vreg("t2", "t2", is_wide=is_wide) # t2 := fp[CC]
% get_vreg("t1", "t1", is_wide=is_wide) # t1 := fp[BB]
% if divz_throw:
beqz t2, 1f // Must throw before FETCH_ADVANCE_INST.
%#:
FETCH_ADVANCE_INST 2 // advance xPC, load xINST
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
% set_vreg("t1", "t3", z0="t0", is_wide=is_wide)
// fp[AA] := t1
GOTO_OPCODE t2 // continue to next
1:
% if divz_throw:
tail common_errDivideByZero
%#:
// binop wide shift boilerplate
// instr: operands held in t1 (64-bit) and t2 (32-bit), result written to t1.
// instr must not clobber t3.
// Clobbers: t0, t1, t2, t3
//
// Note: Contrary to other -long mathematical operations (which take register pairs for both their
// first and their second source), shl-long, shr-long, and ushr-long take a register pair for their
// first source (the value to be shifted), but a single register for their second source (the
// shifting distance).
%def generic_shift_wide(instr):
FETCH t1, count=1 // t1 := CC|BB
srliw t3, xINST, 8 // t3 := AA
srliw t2, t1, 8 // t2 := CC
and t1, t1, 0xFF // t1 := BB
% get_vreg("t2", "t2") # t2 := fp[CC]
GET_VREG_WIDE t1, t1 // t1 := fp[BB]
FETCH_ADVANCE_INST 2 // advance xPC, load xINST
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
SET_VREG_WIDE t1, t3, z0=t0
// fp[AA] := t1
GOTO_OPCODE t2 // continue to next
//
// binop/2addr vA, vB
// Format 12x: B|A|op
// (see floating_point.S for float/double ops)
//
// add-int/2addr vA, vB
// Format 12x: B|A|b0
%def op_add_int_2addr():
% generic_binop_2addr(instr="addw t1, t1, t2")
// sub-int/2addr vA, vB
// Format 12x: B|A|b1
%def op_sub_int_2addr():
% generic_binop_2addr(instr="subw t1, t1, t2")
// mul-int/2addr vA, vB
// Format 12x: B|A|b2
%def op_mul_int_2addr():
% generic_binop_2addr(instr="mulw t1, t1, t2")
// div-int/2addr vA, vB
// Format 12x: B|A|b3
// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
// ArithmeticException if b == 0.
%def op_div_int_2addr():
% generic_binop_2addr(instr="divw t1, t1, t2", divz_throw=True)
// rem-int/2addr vA, vB
// Format 12x: B|A|b4
// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
// b == 0.
%def op_rem_int_2addr():
% generic_binop_2addr(instr="remw t1, t1, t2", divz_throw=True)
// and-int/2addr vA, vB
// Format 12x: B|A|b5
%def op_and_int_2addr():
% generic_binop_2addr(instr="and t1, t1, t2")
// or-int/2addr vA, vB
// Format 12x: B|A|b6
%def op_or_int_2addr():
% generic_binop_2addr(instr="or t1, t1, t2")
// xor-int/2addr vA, vB
// Format 12x: B|A|b7
%def op_xor_int_2addr():
% generic_binop_2addr(instr="xor t1, t1, t2")
// shl-int/2addr vA, vB
// Format 12x: B|A|b8
%def op_shl_int_2addr():
% generic_binop_2addr(instr="sllw t1, t1, t2")
// shr-int/2addr vA, vB
// Format 12x: B|A|b9
%def op_shr_int_2addr():
% generic_binop_2addr(instr="sraw t1, t1, t2")
// ushr-int/2addr vA, vB
// Format 12x: B|A|ba
%def op_ushr_int_2addr():
% generic_binop_2addr(instr="srlw t1, t1, t2")
// add-long/2addr vA, vB
// Format 12x: B|A|bb
%def op_add_long_2addr():
% generic_binop_2addr(instr="add t1, t1, t2", is_wide=True)
// sub-long/2addr vA, vB
// Format 12x: B|A|bc
%def op_sub_long_2addr():
% generic_binop_2addr(instr="sub t1, t1, t2", is_wide=True)
// mul-long/2addr vA, vB
// Format 12x: B|A|bd
%def op_mul_long_2addr():
% generic_binop_2addr(instr="mul t1, t1, t2", is_wide=True)
// div-long/2addr vA, vB
// Format 12x: B|A|be
%def op_div_long_2addr():
% generic_binop_2addr(instr="div t1, t1, t2", divz_throw=True, is_wide=True)
// rem-long/2addr vA, vB
// Format 12x: B|A|bf
%def op_rem_long_2addr():
% generic_binop_2addr(instr="rem t1, t1, t2", divz_throw=True, is_wide=True)
// and-long/2addr vA, vB
// Format 12x: B|A|c0
%def op_and_long_2addr():
% generic_binop_2addr(instr="and t1, t1, t2", is_wide=True)
// or-long/2addr vA, vB
// Format 12x: B|A|c1
%def op_or_long_2addr():
% generic_binop_2addr(instr="or t1, t1, t2", is_wide=True)
// xor-long/2addr vA, vB
// Format 12x: B|A|c2
%def op_xor_long_2addr():
% generic_binop_2addr(instr="xor t1, t1, t2", is_wide=True)
// shl-long/2addr vA, vB
// Format 12x: B|A|c3
// Note: SLL uses t2[5:0] for the shift amount.
%def op_shl_long_2addr():
% generic_shift_wide_2addr(instr="sll t1, t1, t2")
// shr-long/2addr vA, vB
// Format 12x: B|A|c4
// Note: SRA uses t2[5:0] for the shift amount.
%def op_shr_long_2addr():
% generic_shift_wide_2addr(instr="sra t1, t1, t2")
// ushr-long/2addr vA, vB
// Format 12x: B|A|c5
// Note: SRL uses t2[5:0] for the shift amount.
%def op_ushr_long_2addr():
% generic_shift_wide_2addr(instr="srl t1, t1, t2")
// binop 2addr boilerplate
// instr: operands held in t1 and t2, result written to t1.
// instr must not throw. Exceptions to be thrown prior to instr.
// instr must not clobber t3.
//
// The divz_throw flag generates check-and-throw code for div/0.
// The is_wide flag ensures vregs are read and written in 64-bit widths.
// Clobbers: t0, t1, t2, t3, t4
%def generic_binop_2addr(instr, divz_throw=False, is_wide=False):
srliw t2, xINST, 12 // t2 := B
srliw t3, xINST, 8 // t3 := B|A
% get_vreg("t2", "t2", is_wide=is_wide)
// t2 := fp[B]
and t3, t3, 0xF // t3 := A (cached for SET_VREG)
mv t4, t3 // t4 := A
% get_vreg("t1", "t4", is_wide=is_wide)
// t1 := fp[A]
% if divz_throw:
beqz t2, 1f // Must throw before FETCH_ADVANCE_INST.
%#:
FETCH_ADVANCE_INST 1 // advance xPC, load xINST
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
% set_vreg("t1", "t3", z0="t0", is_wide=is_wide)
// fp[A] := t1
GOTO_OPCODE t2 // continue to next
1:
% if divz_throw:
tail common_errDivideByZero
%#:
// binop wide shift 2addr boilerplate
// instr: operands held in t1 (64-bit) and t2 (32-bit), result written to t1.
// instr must not clobber t3.
// Clobbers: t0, t1, t2, t3, t4
//
// Note: Contrary to other -long/2addr mathematical operations (which take register pairs for both
// their destination/first source and their second source), shl-long/2addr, shr-long/2addr, and
// ushr-long/2addr take a register pair for their destination/first source (the value to be
// shifted), but a single register for their second source (the shifting distance).
%def generic_shift_wide_2addr(instr):
srliw t2, xINST, 12 // t2 := B
srliw t3, xINST, 8 // t3 := B|A
% get_vreg("t2", "t2") # t2 := fp[B]
and t3, t3, 0xF // t3 := A (cached for SET_VREG_WIDE)
mv t4, t3 // t4 := A
FETCH_ADVANCE_INST 1 // advance xPC, load xINST
GET_VREG_WIDE t1, t4 // t1 := fp[A]
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
SET_VREG_WIDE t1, t3, z0=t0
// fp[A] := t1
GOTO_OPCODE t2 // continue to next
//
// binop/lit16 vA, vB, #+CCCC
// Format 22s: B|A|op CCCC
//
// add-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d0 CCCC
%def op_add_int_lit16():
% generic_binop_lit16(instr="addw t1, t1, t2")
// rsub-int vA, vB, #+CCCC
// Format 22s: B|A|d1 CCCC
// Note: rsub-int does not have a suffix since this version is the main opcode of its family.
// Note: Twos-complement reverse subtraction.
%def op_rsub_int():
% generic_binop_lit16(instr="subw t1, t2, t1")
// mul-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d2 CCCC
%def op_mul_int_lit16():
% generic_binop_lit16(instr="mulw t1, t1, t2")
// div-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d3 CCCC
// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
// ArithmeticException if b == 0.
%def op_div_int_lit16():
% generic_binop_lit16(instr="divw t1, t1, t2", divz_throw=True)
// rem-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d4 CCCC
// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
// b == 0.
%def op_rem_int_lit16():
% generic_binop_lit16(instr="remw t1, t1, t2", divz_throw=True)
// and-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d5 CCCC
%def op_and_int_lit16():
% generic_binop_lit16(instr="and t1, t1, t2")
// or-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d6 CCCC
%def op_or_int_lit16():
% generic_binop_lit16(instr="or t1, t1, t2")
// xor-int/lit16 vA, vB, #+CCCC
// Format 22s: B|A|d7 CCCC
%def op_xor_int_lit16():
% generic_binop_lit16(instr="xor t1, t1, t2")
// binop lit16 boilerplate
// instr: operands held in t1 and t2, result written to t1.
// instr must not throw. Exceptions to be thrown prior to instr.
// instr must not clobber t3.
//
// The divz_throw flag generates check-and-throw code for div/0.
// Clobbers: t0, t1, t2, t3
%def generic_binop_lit16(instr, divz_throw=False):
FETCH t2, count=1, signed=1 // t2 := ssssCCCC
srliw t1, xINST, 12 // t1 := B
srliw t3, xINST, 8 // t3 := B|A
% if divz_throw:
beqz t2, 1f // Must throw before FETCH_ADVANCE_INST.
%#:
% get_vreg("t1", "t1") # t1 := fp[B]
and t3, t3, 0xF // t3 := A
FETCH_ADVANCE_INST 2 // advance xPC, load xINST
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
% set_vreg("t1", "t3", z0="t0") # fp[A] := t1
GOTO_OPCODE t2 // continue to next
1:
% if divz_throw:
tail common_errDivideByZero
%#:
//
// binop/lit8 vAA, vBB, #+CC
// Format 22b: AA|op CC|BB
//
// add-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|d8, CC|BB
%def op_add_int_lit8():
% generic_binop_lit8(instr="addw t1, t1, t2")
// rsub-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|d9, CC|BB
// Note: Twos-complement reverse subtraction.
%def op_rsub_int_lit8():
% generic_binop_lit8(instr="subw t1, t2, t1")
// mul-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|da, CC|BB
%def op_mul_int_lit8():
% generic_binop_lit8(instr="mulw t1, t1, t2")
// div-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|db, CC|BB
// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
// ArithmeticException if b == 0.
%def op_div_int_lit8():
% generic_binop_lit8(instr="divw t1, t1, t2", divz_throw=True)
// rem-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|dc, CC|BB
// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
// b == 0.
%def op_rem_int_lit8():
% generic_binop_lit8(instr="remw t1, t1, t2", divz_throw=True)
// and-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|dd, CC|BB
%def op_and_int_lit8():
% generic_binop_lit8(instr="and t1, t1, t2")
// or-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|de, CC|BB
%def op_or_int_lit8():
% generic_binop_lit8(instr="or t1, t1, t2")
// xor-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|df, CC|BB
%def op_xor_int_lit8():
% generic_binop_lit8(instr="xor t1, t1, t2")
// shl-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|e0, CC|BB
// Note: SLLW uses t2[4:0] for the shift amount.
%def op_shl_int_lit8():
% generic_binop_lit8(instr="sllw t1, t1, t2")
// shr-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|e1, CC|BB
// Note: SRAW uses t2[4:0] for the shift amount.
%def op_shr_int_lit8():
% generic_binop_lit8(instr="sraw t1, t1, t2")
// ushr-int/lit8, vAA, vBB, #+CC
// Format 22b: AA|e2, CC|BB
// Note: SRLW uses t2[4:0] for the shift amount.
%def op_ushr_int_lit8():
% generic_binop_lit8(instr="srlw t1, t1, t2")
// binop lit8 boilerplate
// instr: operands held in t1 and t2, result written to t1.
// instr must not throw. Exceptions to be thrown prior to instr.
// instr must not clobber t3.
//
// The divz_throw flag generates check-and-throw code for div/0.
// Clobbers: t0, t1, t2, t3
%def generic_binop_lit8(instr, divz_throw=False):
FETCH t1, count=1, signed=1 // t1 := ssssCC|BB
srliw t3, xINST, 8 // t3 := AA
sraiw t2, t1, 8 // t2 := ssssssCC
andi t1, t1, 0xFF // t1 := BB
% if divz_throw:
beqz t2, 1f // Must throw before FETCH_ADVANCE_INST.
%#:
% get_vreg("t1", "t1") # t1 := fp[BB]
FETCH_ADVANCE_INST 2 // advance xPC, load xINST
$instr // read t1 and t2, write result to t1.
// do not clobber t3!
GET_INST_OPCODE t2 // t2 holds next opcode
% set_vreg("t1", "t3", z0="t0") # fp[AA] := t1
GOTO_OPCODE t2 // continue to next
1:
% if divz_throw:
tail common_errDivideByZero
%#: