9
9
} from "./memory" ;
10
10
import {
11
11
WasmOpcode , WasmSimdOpcode , WasmValtype ,
12
- getOpcodeName ,
12
+ getOpcodeName , MintOpArgType
13
13
} from "./jiterpreter-opcodes" ;
14
14
import {
15
15
MintOpcode , SimdInfo ,
@@ -33,7 +33,7 @@ import {
33
33
34
34
disabledOpcodes , countCallTargets ,
35
35
callTargetCounts ,
36
- trace , traceOnError , traceOnRuntimeError ,
36
+ trace , traceOnError ,
37
37
emitPadding , traceBranchDisplacements ,
38
38
traceEip , nullCheckValidation ,
39
39
traceNullCheckOptimizations ,
@@ -109,6 +109,7 @@ function is_backward_branch_target(
109
109
if ( ! backwardBranchTable )
110
110
return false ;
111
111
112
+ // TODO: sort the table and exploit that for faster scan. Not important yet
112
113
for ( let i = 0 ; i < backwardBranchTable . length ; i ++ ) {
113
114
const actualOffset = ( backwardBranchTable [ i ] * 2 ) + < any > startOfBody ;
114
115
if ( actualOffset === ip )
@@ -128,6 +129,74 @@ function get_known_constant_value(builder: WasmBuilder, localOffset: number): Kn
128
129
return knownConstantValues . get ( localOffset ) ;
129
130
}
130
131
132
+ // Perform a quick scan through the opcodes potentially in this trace to build a table of
133
+ // backwards branch targets, compatible with the layout of the old one that was generated in C.
134
+ // We do this here to match the exact way that the jiterp calculates branch targets, since
135
+ // there were previously corner cases where jiterp and interp disagreed.
136
+ export function generateBackwardBranchTable (
137
+ ip : MintOpcodePtr , startOfBody : MintOpcodePtr , sizeOfBody : MintOpcodePtr ,
138
+ ) : Uint16Array | null {
139
+ const endOfBody = < any > startOfBody + < any > sizeOfBody ;
140
+ // TODO: Cache this table object instance and reuse it to reduce gc pressure?
141
+ const table : number [ ] = [ ] ;
142
+ // IP of the start of the trace in U16s, relative to startOfBody.
143
+ const rbase16 = ( < any > ip - < any > startOfBody ) / 2 ;
144
+
145
+ while ( ip < endOfBody ) {
146
+ // IP of the current opcode in U16s, relative to startOfBody. This is what the back branch table uses
147
+ const rip16 = ( < any > ip - < any > startOfBody ) / 2 ;
148
+ const opcode = < MintOpcode > getU16 ( ip ) ;
149
+ // HACK
150
+ if ( opcode === MintOpcode . MINT_SWITCH )
151
+ break ;
152
+
153
+ const opLengthU16 = cwraps . mono_jiterp_get_opcode_info ( opcode , OpcodeInfoType . Length ) ;
154
+ // Any opcode with a branch argtype will have a decoded displacement, even if we don't
155
+ // implement the opcode. Everything else will return undefined here and be skipped
156
+ const displacement = getBranchDisplacement ( ip , opcode ) ;
157
+ if ( typeof ( displacement ) !== "number" ) {
158
+ ip += < any > ( opLengthU16 * 2 ) ;
159
+ continue ;
160
+ }
161
+
162
+ // These checks shouldn't fail unless memory is corrupted or something is wrong with the decoder.
163
+ // We don't want to cause decoder bugs to make the application exit, though - graceful degradation.
164
+ if ( displacement === 0 ) {
165
+ mono_log_info ( `opcode @${ ip } branch target is self. aborting backbranch table generation` ) ;
166
+ break ;
167
+ }
168
+
169
+ const rtarget16 = rip16 + ( displacement ) ;
170
+ if ( rtarget16 < 0 ) {
171
+ mono_log_info ( `opcode @${ ip } 's displacement of ${ displacement } goes before body: ${ rtarget16 } . aborting backbranch table generation` ) ;
172
+ break ;
173
+ }
174
+
175
+ // If the relative target is before the start of the trace, don't record it.
176
+ // The trace will be unable to successfully branch to it so it would just make the table bigger.
177
+ if ( rtarget16 >= rbase16 )
178
+ table . push ( rtarget16 ) ;
179
+
180
+ switch ( opcode ) {
181
+ case MintOpcode . MINT_CALL_HANDLER :
182
+ case MintOpcode . MINT_CALL_HANDLER_S :
183
+ // While this formally isn't a backward branch target, we want to record
184
+ // the offset of its following instruction so that the jiterpreter knows
185
+ // to generate the necessary dispatch code to enable branching back to it.
186
+ table . push ( rip16 + opLengthU16 ) ;
187
+ break ;
188
+ }
189
+
190
+ ip += < any > ( opLengthU16 * 2 ) ;
191
+ }
192
+
193
+ if ( table . length <= 0 )
194
+ return null ;
195
+ // Not important yet, so not doing it
196
+ // table.sort((a, b) => a - b);
197
+ return new Uint16Array ( table ) ;
198
+ }
199
+
131
200
export function generateWasmBody (
132
201
frame : NativePointer , traceName : string , ip : MintOpcodePtr ,
133
202
startOfBody : MintOpcodePtr , endOfBody : MintOpcodePtr ,
@@ -1546,7 +1615,7 @@ export function generateWasmBody(
1546
1615
}
1547
1616
}
1548
1617
1549
- if ( ( trace > 1 ) || traceOnError || traceOnRuntimeError || mostRecentOptions ! . dumpTraces || instrumentedTraceId ) {
1618
+ if ( ( trace > 1 ) || traceOnError || mostRecentOptions ! . dumpTraces || instrumentedTraceId ) {
1550
1619
let stmtText = `${ ( < any > ip ) . toString ( 16 ) } ${ opname } ` ;
1551
1620
const firstDreg = < any > ip + 2 ;
1552
1621
const firstSreg = firstDreg + ( numDregs * 2 ) ;
@@ -2572,13 +2641,45 @@ function append_call_handler_store_ret_ip(
2572
2641
builder . callHandlerReturnAddresses . push ( retIp ) ;
2573
2642
}
2574
2643
2644
+ function getBranchDisplacement (
2645
+ ip : MintOpcodePtr , opcode : MintOpcode
2646
+ ) : number | undefined {
2647
+ const opArgType = cwraps . mono_jiterp_get_opcode_info ( opcode , OpcodeInfoType . OpArgType ) ,
2648
+ payloadOffset = cwraps . mono_jiterp_get_opcode_info ( opcode , OpcodeInfoType . Sregs ) ,
2649
+ payloadAddress = < any > ip + 2 + ( payloadOffset * 2 ) ;
2650
+
2651
+ let result : number ;
2652
+ switch ( opArgType ) {
2653
+ case MintOpArgType . MintOpBranch :
2654
+ result = getI32_unaligned ( payloadAddress ) ;
2655
+ break ;
2656
+ case MintOpArgType . MintOpShortBranch :
2657
+ result = getI16 ( payloadAddress ) ;
2658
+ break ;
2659
+ case MintOpArgType . MintOpShortAndShortBranch :
2660
+ result = getI16 ( payloadAddress + 2 ) ;
2661
+ break ;
2662
+ default :
2663
+ return undefined ;
2664
+ }
2665
+
2666
+ if ( traceBranchDisplacements )
2667
+ mono_log_info ( `${ getOpcodeName ( opcode ) } @${ ip } displacement=${ result } ` ) ;
2668
+
2669
+ return result ;
2670
+ }
2671
+
2575
2672
function emit_branch (
2576
2673
builder : WasmBuilder , ip : MintOpcodePtr ,
2577
- frame : NativePointer , opcode : MintOpcode , displacement ?: number
2674
+ frame : NativePointer , opcode : MintOpcode
2578
2675
) : boolean {
2579
2676
const isSafepoint = ( opcode >= MintOpcode . MINT_BRFALSE_I4_SP ) &&
2580
2677
( opcode <= MintOpcode . MINT_BLT_UN_I8_IMM_SP ) ;
2581
2678
2679
+ const displacement = getBranchDisplacement ( ip , opcode ) ;
2680
+ if ( typeof ( displacement ) !== "number" )
2681
+ return false ;
2682
+
2582
2683
// If the branch is taken we bail out to allow the interpreter to do it.
2583
2684
// So for brtrue, we want to do 'cond == 0' to produce a bailout only
2584
2685
// when the branch will be taken (by skipping the bailout in this block)
@@ -2592,15 +2693,7 @@ function emit_branch(
2592
2693
case MintOpcode . MINT_BR_S : {
2593
2694
const isCallHandler = ( opcode === MintOpcode . MINT_CALL_HANDLER ) ||
2594
2695
( opcode === MintOpcode . MINT_CALL_HANDLER_S ) ;
2595
- displacement = (
2596
- ( opcode === MintOpcode . MINT_BR ) ||
2597
- ( opcode === MintOpcode . MINT_CALL_HANDLER )
2598
- )
2599
- ? getArgI32 ( ip , 1 )
2600
- : getArgI16 ( ip , 1 ) ;
2601
2696
2602
- if ( traceBranchDisplacements )
2603
- mono_log_info ( `br.s @${ ip } displacement=${ displacement } ` ) ;
2604
2697
const destination = < any > ip + ( displacement * 2 ) ;
2605
2698
2606
2699
if ( displacement <= 0 ) {
@@ -2653,7 +2746,6 @@ function emit_branch(
2653
2746
2654
2747
// Load the condition
2655
2748
2656
- displacement = getArgI16 ( ip , 2 ) ;
2657
2749
append_ldloc ( builder , getArgU16 ( ip , 1 ) , is64 ? WasmOpcode . i64_load : WasmOpcode . i32_load ) ;
2658
2750
if (
2659
2751
( opcode === MintOpcode . MINT_BRFALSE_I4_S ) ||
@@ -2684,11 +2776,6 @@ function emit_branch(
2684
2776
}
2685
2777
}
2686
2778
2687
- if ( ! displacement )
2688
- throw new Error ( "Branch had no displacement" ) ;
2689
- else if ( traceBranchDisplacements )
2690
- mono_log_info ( `${ getOpcodeName ( opcode ) } @${ ip } displacement=${ displacement } ` ) ;
2691
-
2692
2779
const destination = < any > ip + ( displacement * 2 ) ;
2693
2780
2694
2781
if ( displacement < 0 ) {
@@ -2741,10 +2828,6 @@ function emit_relop_branch(
2741
2828
if ( ! relopInfo && ! intrinsicFpBinop )
2742
2829
return false ;
2743
2830
2744
- const displacement = getArgI16 ( ip , 3 ) ;
2745
- if ( traceBranchDisplacements )
2746
- mono_log_info ( `relop @${ ip } displacement=${ displacement } ` ) ;
2747
-
2748
2831
const operandLoadOp = relopInfo
2749
2832
? relopInfo [ 1 ]
2750
2833
: (
@@ -2779,7 +2862,7 @@ function emit_relop_branch(
2779
2862
builder . callImport ( "relop_fp" ) ;
2780
2863
}
2781
2864
2782
- return emit_branch ( builder , ip , frame , opcode , displacement ) ;
2865
+ return emit_branch ( builder , ip , frame , opcode ) ;
2783
2866
}
2784
2867
2785
2868
function emit_math_intrinsic ( builder : WasmBuilder , ip : MintOpcodePtr , opcode : MintOpcode ) : boolean {
0 commit comments