Skip to content

Commit 9e5eac8

Browse files
committed
Fixed a chance of CBRANCH looping
Fixed CBRANCH jump probability being lower than expected
1 parent 5241cb9 commit 9e5eac8

8 files changed

+73
-46
lines changed

doc/program.asm

+30-30
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ randomx_isn_5:
1818
xchg r12, r8
1919
randomx_isn_6:
2020
; CBRANCH -188214077, COND 5
21-
add r9, -188214045
22-
test r9, 8160
21+
add r9, -188209981
22+
test r9, 2088960
2323
jz randomx_isn_0
2424
randomx_isn_7:
2525
; ISTORE L3[r0-784322734], r3
@@ -52,13 +52,13 @@ randomx_isn_12:
5252
imul r15, r12
5353
randomx_isn_13:
5454
; CBRANCH 179989705, COND 3
55-
add r8, 179989705
56-
test r8, 2040
55+
add r8, 179988681
56+
test r8, 522240
5757
jz randomx_isn_7
5858
randomx_isn_14:
5959
; CBRANCH 1801296358, COND 3
60-
add r10, 1801296366
61-
test r10, 2040
60+
add r10, 1801296358
61+
test r10, 522240
6262
jz randomx_isn_14
6363
randomx_isn_15:
6464
; IADD_RS r6, r2, SHFT 3
@@ -80,8 +80,8 @@ randomx_isn_19:
8080
mulpd xmm5, xmm10
8181
randomx_isn_20:
8282
; CBRANCH 1593588996, COND 3
83-
add r11, 1593589004
84-
test r11, 2040
83+
add r11, 1593587972
84+
test r11, 522240
8585
jz randomx_isn_15
8686
randomx_isn_21:
8787
; IROR_R r7, r2
@@ -102,7 +102,7 @@ randomx_isn_23:
102102
randomx_isn_24:
103103
; CBRANCH 149087159, COND 13
104104
add r12, 149087159
105-
test r12, 2088960
105+
test r12, 534773760
106106
jz randomx_isn_21
107107
randomx_isn_25:
108108
; FADD_R f3, a0
@@ -208,8 +208,8 @@ randomx_isn_50:
208208
subpd xmm3, xmm8
209209
randomx_isn_51:
210210
; CBRANCH -1975981803, COND 14
211-
add r9, -1975981803
212-
test r9, 4177920
211+
add r9, -1973884651
212+
test r9, 1069547520
213213
jz randomx_isn_25
214214
randomx_isn_52:
215215
; IADD_M r1, L3[1622792]
@@ -219,8 +219,8 @@ randomx_isn_53:
219219
subpd xmm2, xmm8
220220
randomx_isn_54:
221221
; CBRANCH 1917049931, COND 12
222-
add r13, 1917049931
223-
test r13, 1044480
222+
add r13, 1918098507
223+
test r13, 267386880
224224
jz randomx_isn_52
225225
randomx_isn_55:
226226
; IXOR_R r2, r3
@@ -249,7 +249,7 @@ randomx_isn_61:
249249
randomx_isn_62:
250250
; CBRANCH 1111898647, COND 1
251251
add r14, 1111898647
252-
test r14, 510
252+
test r14, 130560
253253
jz randomx_isn_55
254254
randomx_isn_63:
255255
; IMUL_R r6, r5
@@ -288,8 +288,8 @@ randomx_isn_73:
288288
mulpd xmm4, xmm8
289289
randomx_isn_74:
290290
; CBRANCH -1200328848, COND 4
291-
add r15, -1200328848
292-
test r15, 4080
291+
add r15, -1200326800
292+
test r15, 1044480
293293
jz randomx_isn_63
294294
randomx_isn_75:
295295
; FSQRT_R e0
@@ -346,8 +346,8 @@ randomx_isn_88:
346346
imul r9, qword ptr [rsi+rax]
347347
randomx_isn_89:
348348
; CBRANCH -122257389, COND 13
349-
add r8, -122249197
350-
test r8, 2088960
349+
add r8, -123305965
350+
test r8, 534773760
351351
jz randomx_isn_75
352352
randomx_isn_90:
353353
; ISTORE L1[r5+228116180], r7
@@ -481,8 +481,8 @@ randomx_isn_122:
481481
subpd xmm0, xmm9
482482
randomx_isn_123:
483483
; CBRANCH 269211216, COND 3
484-
add r9, 269211224
485-
test r9, 2040
484+
add r9, 269212240
485+
test r9, 522240
486486
jz randomx_isn_100
487487
randomx_isn_124:
488488
; FSUB_M f2, L1[r6-1615966581]
@@ -564,8 +564,8 @@ randomx_isn_142:
564564
addpd xmm1, xmm8
565565
randomx_isn_143:
566566
; CBRANCH 880467599, COND 5
567-
add r14, 880467631
568-
test r14, 8160
567+
add r14, 880471695
568+
test r14, 2088960
569569
jz randomx_isn_124
570570
randomx_isn_144:
571571
; FMUL_R e1, a1
@@ -585,8 +585,8 @@ randomx_isn_147:
585585
add r9, qword ptr [rsi+rax]
586586
randomx_isn_148:
587587
; CBRANCH -1843326985, COND 14
588-
add r10, -1843310601
589-
test r10, 4177920
588+
add r10, -1841229833
589+
test r10, 1069547520
590590
jz randomx_isn_144
591591
randomx_isn_149:
592592
; IADD_RS r4, r3, SHFT 2
@@ -655,8 +655,8 @@ randomx_isn_163:
655655
shufpd xmm3, xmm3, 1
656656
randomx_isn_164:
657657
; CBRANCH -2107581963, COND 4
658-
add r11, -2107581963
659-
test r11, 4080
658+
add r11, -2107584011
659+
test r11, 1044480
660660
jz randomx_isn_149
661661
randomx_isn_165:
662662
; FSUB_R f1, a2
@@ -720,8 +720,8 @@ randomx_isn_180:
720720
subpd xmm3, xmm9
721721
randomx_isn_181:
722722
; CBRANCH 556152230, COND 12
723-
add r12, 556152230
724-
test r12, 1044480
723+
add r12, 557200806
724+
test r12, 267386880
725725
jz randomx_isn_165
726726
randomx_isn_182:
727727
; FSQRT_R e2
@@ -956,8 +956,8 @@ randomx_isn_246:
956956
imul r15, r10
957957
randomx_isn_247:
958958
; CBRANCH -8545330, COND 4
959-
add r8, -8545314
960-
test r8, 4080
959+
add r8, -8547378
960+
test r8, 1044480
961961
jz randomx_isn_213
962962
randomx_isn_248:
963963
; ISTORE L1[r0+1951752498], r5

doc/specs.md

+20-7
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ RandomX has several configurable parameters that are listed in Table 1.2.1 with
5555
|`RANDOMX_PROGRAM_SIZE`|The number of instructions in a RandomX program|`256`|
5656
|`RANDOMX_PROGRAM_ITERATIONS`|The number of iterations per program|`2048`|
5757
|`RANDOMX_PROGRAM_COUNT`|The number of programs per hash|`8`|
58-
|`RANDOMX_JUMP_BITS`|How many register bits must be zero for the CBRANCH instruction to jump|`8`|
58+
|`RANDOMX_JUMP_BITS`|Jump condition mask size in bits|`8`|
59+
|`RANDOMX_JUMP_OFFSET`|Jump condition mask offset in bits|`8`|
5960
|`RANDOMX_SCRATCHPAD_L3`|Scratchpad L3 size in bytes|`2097152`|
6061
|`RANDOMX_SCRATCHPAD_L2`|Scratchpad L2 size in bytes|`262144`|
6162
|`RANDOMX_SCRATCHPAD_L1`|Scratchpad L1 size in bytes|`16384`|
@@ -613,16 +614,28 @@ A register is considered as modified by an instruction in the following cases:
613614
There are 3 rules for the selection of the `creg` register, evaluated in this order:
614615

615616
1. The register with the lowest value of `lastUsed` tag is selected.
616-
2. In case multiple registers have the same value of the `lastUsed` tag, the register with the lowest value of the `count` tag is selected.
617-
3. In case multiple registers have the same values of both `lastUsed` and `count` tags, a register with the lowest index is selected (`r0` before `r1` etc.).
617+
1. In case multiple registers have the same value of the `lastUsed` tag, the register with the lowest value of the `count` tag is selected.
618+
1. In case multiple registers have the same values of both `lastUsed` and `count` tags, a register with the lowest index is selected (`r0` before `r1` etc.).
618619

619620
Whenever a register is selected as the operand of a CBRANCH instruction, its `count` tag is increased by 1.
620621

621-
The CBRANCH instruction performs the following steps (`|` represents a bitwise OR operation, `&` is a bitwise AND operation):
622+
The CBRANCH instruction performs the following steps:
622623

623-
1. A constant value of `imm32 | (1 << mod.cond)` is added to `creg`.
624-
2. `conditionMask` is constructed as `RANDOMX_JUMP_BITS` one-bits shifted left by `mod.cond`.
625-
3. If `creg & conditionMask` is zero, execution jumps to instruction `creg.lastUsed + 1` (the instruction following the instruction where `creg` was last modified).
624+
1. A constant `b` is calculated as `mod.cond + RANDOMX_JUMP_OFFSET`.
625+
1. A constant `conditionImmediate` is constructed as sign-extended `imm32` with bit `b` set to 1 and bit `b-1` set to 0 (if `b > 0`).
626+
1. `conditionImmediate` is added to `creg`.
627+
1. If bits `b` to `b + RANDOMX_JUMP_BITS - 1` of `creg` are zero, execution jumps to instruction `creg.lastUsed + 1` (the instruction following the instruction where `creg` was last modified).
628+
629+
Bits in immediate and register values are numbered from 0 to 63 with 0 being the least significant bit. For example, for `b = 10` and `RANDOMX_JUMP_BITS = 8`, the bits are arranged like this:
630+
631+
```
632+
conditionImmediate = SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSMMMMMMMMMMMMMMMMMMMMM10MMMMMMMMM
633+
creg = ..............................................XXXXXXXX..........
634+
```
635+
636+
`S` is a copied sign bit from `imm32`. `M` denotes bits of `imm32`. The 9th bit is set to 0 and the 10th bit is set to 1. This value would be added to `creg`.
637+
638+
The second line uses `X` to mark bits of `creg` that would be checked by the condition. If all these bits are 0 after adding `conditionImmediate`, the jump is executed.
626639

627640
The construction of the CBRANCH instruction ensures that no inifinite loops are possible in the program.
628641

src/assembly_generator_x86.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,11 @@ namespace randomx {
532532
int reg = getConditionRegister(registerUsage);
533533
int target = registerUsage[reg].lastUsed + 1;
534534
registerUsage[reg].count++;
535-
int shift = instr.getModCond();
536-
asmCode << "\tadd " << regR[reg] << ", " << (int32_t)(instr.getImm32() | (1 << shift)) << std::endl;
535+
int shift = instr.getModCond() + ConditionOffset;
536+
int32_t imm = instr.getImm32() | (1L << shift);
537+
if (ConditionOffset > 0 || shift > 0)
538+
imm &= ~(1L << (shift - 1));
539+
asmCode << "\tadd " << regR[reg] << ", " << imm << std::endl;
537540
asmCode << "\ttest " << regR[reg] << ", " << (ConditionMask << shift) << std::endl;
538541
asmCode << "\tjz randomx_isn_" << target << std::endl;
539542
//mark all registers as used

src/common.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ namespace randomx {
4141
static_assert(RANDOMX_SCRATCHPAD_L2 >= RANDOMX_SCRATCHPAD_L1, "RANDOMX_SCRATCHPAD_L2 must be greater than or equal to RANDOMX_SCRATCHPAD_L1.");
4242
static_assert((RANDOMX_SCRATCHPAD_L1 & (RANDOMX_SCRATCHPAD_L1 - 1)) == 0, "RANDOMX_SCRATCHPAD_L1 must be a power of 2.");
4343
static_assert(RANDOMX_CACHE_ACCESSES > 1, "RANDOMX_CACHE_ACCESSES must be greater than 1");
44-
static_assert(RANDOMX_JUMP_BITS >= 1 && RANDOMX_JUMP_BITS <= 16, "RANDOMX_JUMP_BITS must be an integer in the range 1-16.");
44+
static_assert(RANDOMX_JUMP_BITS > 0, "RANDOMX_JUMP_BITS must be greater than 0.");
45+
static_assert(RANDOMX_JUMP_OFFSET >= 0, "RANDOMX_JUMP_OFFSET must be greater than or equal to 0.");
46+
static_assert(RANDOMX_JUMP_BITS + RANDOMX_JUMP_OFFSET <= 16, "RANDOMX_JUMP_BITS + RANDOMX_JUMP_OFFSET must not exceed 16.");
4547

4648
constexpr int wtSum = RANDOMX_FREQ_IADD_RS + RANDOMX_FREQ_IADD_M + RANDOMX_FREQ_ISUB_R + \
4749
RANDOMX_FREQ_ISUB_M + RANDOMX_FREQ_IMUL_R + RANDOMX_FREQ_IMUL_M + RANDOMX_FREQ_IMULH_R + \
@@ -62,6 +64,7 @@ namespace randomx {
6264
constexpr uint64_t DatasetSize = RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE;
6365
constexpr uint32_t DatasetExtraItems = RANDOMX_DATASET_EXTRA_SIZE / RANDOMX_DATASET_ITEM_SIZE;
6466
constexpr uint32_t ConditionMask = ((1 << RANDOMX_JUMP_BITS) - 1);
67+
constexpr int ConditionOffset = RANDOMX_JUMP_OFFSET;
6568
constexpr int StoreL3Condition = 14;
6669

6770
#ifdef TRACE

src/configuration.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
6464
//Scratchpad L1 size in bytes. Must be a power of two and less than or equal to RANDOMX_SCRATCHPAD_L2.
6565
#define RANDOMX_SCRATCHPAD_L1 (16 * 1024)
6666

67-
//How many register bits must be zero for CBRANCH instruction to jump. Must be an integer in the range 1-16.
67+
//Jump condition mask size in bits.
6868
#define RANDOMX_JUMP_BITS 8
6969

70+
//Jump condition mask offset in bits.
71+
#define RANDOMX_JUMP_OFFSET 8
72+
7073
/*
7174
Instruction frequencies (per 256 opcodes)
7275
Total sum of frequencies must be 256

src/jit_compiler_x86.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -775,10 +775,13 @@ namespace randomx {
775775
int reg = getConditionRegister(registerUsage);
776776
int target = registerUsage[reg].lastUsed + 1;
777777
registerUsage[reg].count++;
778-
int shift = instr.getModCond();
779778
emit(REX_ADD_I);
780779
emitByte(0xc0 + reg);
781-
emit32(instr.getImm32() | (1 << shift));
780+
int shift = instr.getModCond() + ConditionOffset;
781+
uint32_t imm = instr.getImm32() | (1UL << shift);
782+
if (ConditionOffset > 0 || shift > 0)
783+
imm &= ~(1UL << (shift - 1));
784+
emit32(imm);
782785
emit(REX_TEST);
783786
emitByte(0xc0 + reg);
784787
emit32(ConditionMask << shift);

src/tests/benchmark.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ int main(int argc, char** argv) {
229229
std::cout << "Calculated result: ";
230230
result.print(std::cout);
231231
if (noncesCount == 1000 && seedValue == 0)
232-
std::cout << "Reference result: a15448785857f9a78703eb5da235dfe73d0d5fc4c8effaebe73869904f5af47d" << std::endl;
232+
std::cout << "Reference result: 47452f6064db799ae580dd71fe0ebe221579cedf837fac7095f1c5edc07cf345" << std::endl;
233233
if (!miningMode) {
234234
std::cout << "Performance: " << 1000 * elapsed / noncesCount << " ms per hash" << std::endl;
235235
}

src/vm_interpreted.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -615,9 +615,11 @@ namespace randomx {
615615
ibc.isrc = &r[reg];
616616
ibc.target = registerUsage[reg].lastUsed;
617617
registerUsage[reg].count++;
618-
int shift = instr.getModCond();
619-
const uint64_t conditionMask = ConditionMask << instr.getModCond();
618+
int shift = instr.getModCond() + ConditionOffset;
619+
const uint64_t conditionMask = ConditionMask << shift;
620620
ibc.imm = signExtend2sCompl(instr.getImm32()) | (1ULL << shift);
621+
if (ConditionOffset > 0 || shift > 0) //clear the bit below the condition mask - this limits the number of successive jumps to 2
622+
ibc.imm &= ~(1ULL << (shift - 1));
621623
ibc.memMask = ConditionMask << shift;
622624
//mark all registers as used
623625
for (unsigned j = 0; j < RegistersCount; ++j) {

0 commit comments

Comments
 (0)