Skip to content

Commit 9aa5515

Browse files
committed
Implement MULH, MULHU, MULHSU
1 parent 8350953 commit 9aa5515

File tree

3 files changed

+93
-23
lines changed

3 files changed

+93
-23
lines changed

src/kriscv/kdist/riscv-semantics/riscv.md

+11
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,17 @@ The following instructions behave analogously to their `I`-suffixed counterparts
258258
rule <instrs> MUL RD , RS1 , RS2 => .K ...</instrs>
259259
<regs> REGS => writeReg(REGS, RD, readReg(REGS, RS1) *Word readReg(REGS, RS2)) </regs>
260260
```
261+
`MULU`, `MULHU`, `MULHSU` give the higher `XLEN` bits after multiplication, with signedness of the operands interpreted accordingly.
262+
```k
263+
rule <instrs> MULH RD , RS1 , RS2 => .K ...</instrs>
264+
<regs> REGS => writeReg(REGS, RD, readReg(REGS, RS1) *hWord readReg(REGS, RS2)) </regs>
265+
266+
rule <instrs> MULHU RD , RS1 , RS2 => .K ...</instrs>
267+
<regs> REGS => writeReg(REGS, RD, readReg(REGS, RS1) *huWord readReg(REGS, RS2)) </regs>
268+
269+
rule <instrs> MULHSU RD , RS1 , RS2 => .K ...</instrs>
270+
<regs> REGS => writeReg(REGS, RD, readReg(REGS, RS1) *hsuWord readReg(REGS, RS2)) </regs>
271+
```
261272
`SLL`, `SRL`, and `SRA` read their shift amount fom the lowest `log_2(XLEN)` bits of `RS2`.
262273
```k
263274
rule <instrs> SLL RD , RS1 , RS2 => .K ...</instrs>

src/kriscv/kdist/riscv-semantics/word.md

+11
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ The same is true for the `XLEN` least-significant bits of the result of multipli
7474
syntax Word ::= Word "*Word" Word [function, total, symbol(mulWord)]
7575
rule W(I1) *Word W(I2) => chop(I1 *Int I2)
7676
```
77+
The value of the upper `XLEN` bits however depends on signedness of the operands, as reflected by the followig functions.
78+
```k
79+
syntax Word ::= Word "*hWord" Word [function, total, symbol(mulhWord)]
80+
rule W1 *hWord W2 => chop((Word2SInt(W1) *Int Word2SInt(W2)) >>Int XLEN)
81+
82+
syntax Word ::= Word "*huWord" Word [function, total, symbol(mulhuWord)]
83+
rule W(I1) *huWord W(I2) => chop((I1 *Int I2) >>Int XLEN)
84+
85+
syntax Word ::= Word "*hsuWord" Word [function, total, symbol(mulhsuWord)]
86+
rule W1 *hsuWord W(I2) => chop((Word2SInt(W1) *Int I2) >>Int XLEN)
87+
```
7788
Above, the `chop` utility function
7889
```k
7990
syntax Word ::= chop(Int) [function, total]

src/tests/integration/test_functions.py

+71-23
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ def definition_dir() -> Path:
2121
return kdist.get('riscv-semantics.func-test')
2222

2323

24+
def _test_function(definition_dir: path, app: Pattern, res: Pattern) -> None:
25+
# Given
26+
init = config(app)
27+
expected = config(res)
28+
29+
# When
30+
actual = llvm_interpret(definition_dir, init)
31+
32+
# Then
33+
assert actual == expected
34+
35+
2436
def config(kitem: Pattern) -> App:
2537
return generated_top(
2638
(
@@ -59,20 +71,13 @@ def reg(x: int) -> App:
5971
ids=[test_id for test_id, *_ in DISASSEMBLE_TEST_DATA],
6072
)
6173
def test_disassemble(definition_dir: Path, test_id: str, code: bytes, sort: SortApp, pattern: Pattern) -> None:
62-
# Given
63-
n = int.from_bytes(code, byteorder='big')
64-
init = config(inj(SortApp('SortInstruction'), SORT_K_ITEM, disassemble(n)))
65-
expected = config(inj(sort, SORT_K_ITEM, pattern))
66-
67-
# When
68-
actual = llvm_interpret(definition_dir, init)
69-
70-
# Then
71-
assert actual == expected
72-
74+
def disassemble(n: int) -> App:
75+
return App('Lbldisassemble', (), (int_dv(n),))
7376

74-
def disassemble(n: int) -> App:
75-
return App('Lbldisassemble', (), (int_dv(n),))
77+
n = int.from_bytes(code, byteorder='big')
78+
app = inj(SortApp('SortInstruction'), SORT_K_ITEM, disassemble(n))
79+
res = inj(sort, SORT_K_ITEM, pattern)
80+
_test_function(definition_dir=definition_dir, app=app, res=res)
7681

7782

7883
def is_32bit(x: int) -> bool:
@@ -104,19 +109,62 @@ def signed(x: int) -> int:
104109
assert all(is_32bit(op1) and is_32bit(op2) for op1, op2 in MUL_TEST_DATA)
105110

106111

112+
def _test_mul(
113+
definition_dir: Path,
114+
symbol: str,
115+
op1: int,
116+
op2: int,
117+
res: int,
118+
) -> None:
119+
def w(x: int) -> App:
120+
return App('LblW', (), (int_dv(x),))
121+
122+
_test_function(
123+
definition_dir=definition_dir,
124+
app=inj(SortApp('SortWord'), SORT_K_ITEM, App(symbol, (), (w(op1), w(op2)))),
125+
res=inj(SortApp('SortWord'), SORT_K_ITEM, w(res))
126+
)
127+
128+
107129
@pytest.mark.parametrize('op1,op2', MUL_TEST_DATA, ids=count())
108130
def test_mul(definition_dir: Path, op1: int, op2: int) -> None:
109-
# Given
110-
init = config(inj(SortApp('SortWord'), SORT_K_ITEM, App('LblmulWord', (), (w(op1), w(op2)))))
111-
res = chop(op1 * op2)
112-
expected = config(inj(SortApp('SortWord'), SORT_K_ITEM, w(res)))
131+
_test_mul(
132+
definition_dir=definition_dir,
133+
symbol='LblmulWord',
134+
op1=op1,
135+
op2=op2,
136+
res=chop(op1 * op2),
137+
)
113138

114-
# When
115-
actual = llvm_interpret(definition_dir, init)
116139

117-
# Then
118-
assert actual == expected
140+
@pytest.mark.parametrize('op1,op2', MUL_TEST_DATA, ids=count())
141+
def test_mulh(definition_dir: Path, op1: int, op2: int) -> None:
142+
_test_mul(
143+
definition_dir=definition_dir,
144+
symbol='LblmulhWord',
145+
op1=op1,
146+
op2=op2,
147+
res=chop((signed(op1) * signed(op2)) >> 32),
148+
)
119149

120150

121-
def w(x: int) -> App:
122-
return App('LblW', (), (int_dv(x),))
151+
@pytest.mark.parametrize('op1,op2', MUL_TEST_DATA, ids=count())
152+
def test_mulhu(definition_dir: Path, op1: int, op2: int) -> None:
153+
_test_mul(
154+
definition_dir=definition_dir,
155+
symbol='LblmulhuWord',
156+
op1=op1,
157+
op2=op2,
158+
res=chop((op1 * op2) >> 32),
159+
)
160+
161+
162+
@pytest.mark.parametrize('op1,op2', MUL_TEST_DATA, ids=count())
163+
def test_mulhsu(definition_dir: Path, op1: int, op2: int) -> None:
164+
_test_mul(
165+
definition_dir=definition_dir,
166+
symbol='LblmulhsuWord',
167+
op1=op1,
168+
op2=op2,
169+
res=chop((signed(op1) * op2) >> 32),
170+
)

0 commit comments

Comments
 (0)