1
1
/*
2
- * Copyright (c) 2023, 2024 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2023, 2025 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
26
26
package jdk .graal .compiler .lir .amd64 ;
27
27
28
28
import static jdk .graal .compiler .asm .amd64 .AMD64Assembler .SSEOp .UCOMIS ;
29
+ import static jdk .graal .compiler .asm .amd64 .AMD64Assembler .SSEOp .XOR ;
30
+ import static jdk .graal .compiler .asm .amd64 .AMD64Assembler .VexRMOp .VUCOMISD ;
31
+ import static jdk .graal .compiler .asm .amd64 .AMD64Assembler .VexRMOp .VUCOMISS ;
32
+ import static jdk .graal .compiler .asm .amd64 .AMD64Assembler .VexRVMOp .VPXOR ;
29
33
import static jdk .graal .compiler .asm .amd64 .AMD64BaseAssembler .OperandSize ;
34
+ import static jdk .graal .compiler .asm .amd64 .AVXKind .AVXSize .XMM ;
30
35
import static jdk .graal .compiler .lir .LIRInstruction .OperandFlag .ILLEGAL ;
31
36
import static jdk .graal .compiler .lir .LIRInstruction .OperandFlag .REG ;
32
37
import static jdk .vm .ci .code .ValueUtil .asRegister ;
33
38
34
39
import jdk .graal .compiler .asm .Label ;
35
40
import jdk .graal .compiler .asm .amd64 .AMD64Address ;
36
41
import jdk .graal .compiler .asm .amd64 .AMD64Assembler ;
42
+ import jdk .graal .compiler .asm .amd64 .AMD64Assembler .AMD64SIMDInstructionEncoding ;
37
43
import jdk .graal .compiler .asm .amd64 .AMD64MacroAssembler ;
38
- import jdk .graal .compiler .asm .amd64 .AVXKind ;
39
44
import jdk .graal .compiler .debug .GraalError ;
40
45
import jdk .graal .compiler .lir .LIRInstructionClass ;
41
46
import jdk .graal .compiler .lir .asm .CompilationResultBuilder ;
42
47
import jdk .graal .compiler .lir .gen .LIRGeneratorTool ;
48
+ import jdk .vm .ci .amd64 .AMD64 ;
43
49
import jdk .vm .ci .amd64 .AMD64Kind ;
44
50
import jdk .vm .ci .code .Register ;
45
51
import jdk .vm .ci .meta .JavaConstant ;
@@ -58,26 +64,51 @@ public class AMD64ConvertFloatToIntegerOp extends AMD64LIRInstruction {
58
64
59
65
@ Def ({REG }) protected Value dstValue ;
60
66
@ Alive ({REG }) protected Value srcValue ;
61
- @ Temp ({REG , ILLEGAL }) protected Value tmpValue ;
67
+ @ Temp ({REG , ILLEGAL }) protected Value zeroTmp ;
62
68
63
- private final OpcodeEmitter opcode ;
69
+ private final OpcodeEmitter opcodeEmitter ;
64
70
private final boolean canBeNaN ;
65
71
private final boolean canOverflow ;
72
+ private final int integerBytes ;
73
+ /**
74
+ * The size of the input float operand. We only emit scalar instructions, but UCOMIS wants to be
75
+ * encoded with a packed size.
76
+ */
77
+ private final OperandSize packedSize ;
78
+ /** The size of the input float operand. */
79
+ private final OperandSize scalarSize ;
80
+ private final AMD64SIMDInstructionEncoding encoding ;
66
81
67
82
@ FunctionalInterface
68
83
public interface OpcodeEmitter {
69
84
/** Emit the actual conversion instruction. */
70
85
void emit (CompilationResultBuilder crb , AMD64MacroAssembler masm , Register dst , Register src );
71
86
}
72
87
73
- public AMD64ConvertFloatToIntegerOp (LIRGeneratorTool tool , OpcodeEmitter opcode , Value dstValue , Value srcValue , boolean canBeNaN , boolean canOverflow ) {
88
+ public AMD64ConvertFloatToIntegerOp (LIRGeneratorTool tool , OpcodeEmitter opcodeEmitter , Value dstValue , Value srcValue , boolean canBeNaN ,
89
+ boolean canOverflow ) {
74
90
super (TYPE );
91
+ this .opcodeEmitter = opcodeEmitter ;
75
92
this .dstValue = dstValue ;
76
93
this .srcValue = srcValue ;
77
- this .opcode = opcode ;
78
- this .tmpValue = canOverflow ? tool .newVariable (srcValue .getValueKind ()) : Value .ILLEGAL ;
94
+ this .zeroTmp = canOverflow ? tool .newVariable (srcValue .getValueKind ()) : Value .ILLEGAL ;
79
95
this .canBeNaN = canBeNaN ;
80
96
this .canOverflow = canOverflow ;
97
+ this .integerBytes = dstValue .getPlatformKind ().getSizeInBytes ();
98
+ GraalError .guarantee (integerBytes == 4 || integerBytes == 8 , "unexpected target %s" , dstValue );
99
+ switch (srcValue .getPlatformKind ().getSizeInBytes ()) {
100
+ case 4 :
101
+ this .packedSize = OperandSize .PS ;
102
+ this .scalarSize = OperandSize .SS ;
103
+ break ;
104
+ case 8 :
105
+ this .packedSize = OperandSize .PD ;
106
+ this .scalarSize = OperandSize .SD ;
107
+ break ;
108
+ default :
109
+ throw GraalError .shouldNotReachHere ("unexpected input %s" .formatted (srcValue ));
110
+ }
111
+ this .encoding = AMD64SIMDInstructionEncoding .forFeatures (((AMD64 ) tool .target ().arch ).getFeatures ());
81
112
82
113
GraalError .guarantee (srcValue .getPlatformKind () instanceof AMD64Kind kind && kind .getVectorLength () == 1 && kind .isXMM (), "source must be scalar floating-point: %s" , srcValue );
83
114
GraalError .guarantee (dstValue .getPlatformKind () instanceof AMD64Kind kind && kind .getVectorLength () == 1 && kind .isInteger (), "destination must be integer: %s" , dstValue );
@@ -90,21 +121,13 @@ public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
90
121
Label fixupPath = new Label ();
91
122
Label done = new Label ();
92
123
93
- opcode .emit (crb , masm , dst , src );
124
+ opcodeEmitter .emit (crb , masm , dst , src );
94
125
95
126
if (!canBeNaN && !canOverflow ) {
96
127
/* No fixup needed. */
97
128
return ;
98
129
}
99
130
100
- int integerBytes = dstValue .getPlatformKind ().getSizeInBytes ();
101
- GraalError .guarantee (integerBytes == 4 || integerBytes == 8 , "unexpected target %s" , dstValue );
102
- OperandSize floatSize = switch (srcValue .getPlatformKind ().getSizeInBytes ()) {
103
- case 4 -> OperandSize .PS ;
104
- case 8 -> OperandSize .PD ;
105
- default -> throw GraalError .shouldNotReachHere ("unexpected input %s" .formatted (srcValue ));
106
- };
107
-
108
131
/*
109
132
* if (dst == MIN_VALUE) { goto fixupPath; }
110
133
*/
@@ -117,48 +140,67 @@ public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
117
140
118
141
crb .getLIR ().addSlowPath (this , () -> {
119
142
masm .bind (fixupPath );
143
+ emitFixups (masm , src , dst , done );
144
+ masm .jmp (done );
145
+ });
120
146
121
- if (canBeNaN ) {
122
- /*
123
- * if (isNaN(src)) { result = 0; goto done; }
124
- *
125
- * The isNaN check is implemented as src != src. C2's fixup stubs check for a NaN
126
- * bit pattern directly, using the same number of cycles but using an extra general
127
- * purpose register.
128
- */
129
- Label isNotNaN = new Label ();
130
- UCOMIS .emit (masm , floatSize , src , src );
131
- masm .jcc (AMD64Assembler .ConditionFlag .NoParity , isNotNaN , true );
132
- masm .moveInt (dst , 0 );
133
- masm .jmp (done );
134
- masm .bind (isNotNaN );
135
- }
147
+ masm .bind (done );
148
+ }
149
+
150
+ @ SuppressWarnings ("unused" )
151
+ private void emitFixups (AMD64MacroAssembler masm , Register src , Register dst , Label done ) {
152
+ if (canBeNaN ) {
153
+ /*
154
+ * if (isNaN(src)) { result = 0; goto done; }
155
+ *
156
+ * The isNaN check is implemented as src != src. C2's fixup stubs check for a NaN bit
157
+ * pattern directly, using the same number of cycles but using an extra general purpose
158
+ * register.
159
+ */
160
+ Label isNotNaN = new Label ();
161
+ compare (masm , src , src );
162
+ masm .jcc (AMD64Assembler .ConditionFlag .NoParity , isNotNaN , true );
163
+ masm .moveInt (dst , 0 );
164
+ masm .jmp (done );
165
+ masm .bind (isNotNaN );
166
+ }
136
167
137
- if (canOverflow ) {
138
- /*
139
- * if (src > 0.0) { result = MAX_VALUE; }
140
- *
141
- * We use an actual floating point compare, C2's stubs check the sign bit in a GPR.
142
- */
143
- Register zero = asRegister (tmpValue );
144
- masm .pxor (AVXKind .AVXSize .XMM , zero , zero );
145
- UCOMIS .emit (masm , floatSize , src , zero );
146
- masm .jcc (AMD64Assembler .ConditionFlag .BelowEqual , done );
147
- /*
148
- * MAX_VALUE is the bitwise negation of MIN_VALUE, which is already in dst. A
149
- * negation takes the same number of cycles as a move, but its encoding is shorter.
150
- */
151
- if (integerBytes == 4 ) {
152
- masm .notl (dst );
153
- } else {
154
- masm .notq (dst );
155
- }
168
+ if (canOverflow ) {
169
+ /*
170
+ * if (src > 0.0) { result = MAX_VALUE; }
171
+ *
172
+ * We use an actual floating point compare, C2's stubs check the sign bit in a GPR.
173
+ */
174
+ Register zero = asRegister (zeroTmp );
175
+ clearRegister (masm , zero );
176
+ compare (masm , src , zero );
177
+ masm .jcc (AMD64Assembler .ConditionFlag .BelowEqual , done );
178
+ /*
179
+ * MAX_VALUE is the bitwise negation of MIN_VALUE, which is already in dst. A negation
180
+ * takes the same number of cycles as a move, but its encoding is shorter.
181
+ */
182
+ if (integerBytes == 4 ) {
183
+ masm .notl (dst );
184
+ } else {
185
+ masm .notq (dst );
156
186
}
187
+ }
188
+ }
157
189
158
- /* Return to inline code. */
159
- masm .jmp (done );
160
- });
190
+ private void clearRegister (AMD64MacroAssembler masm , Register register ) {
191
+ if (masm .isAVX ()) {
192
+ VPXOR .encoding (encoding ).emit (masm , XMM , register , register , register );
193
+ } else {
194
+ XOR .emit (masm , packedSize , register , register );
195
+ }
196
+ }
161
197
162
- masm .bind (done );
198
+ private void compare (AMD64MacroAssembler masm , Register x , Register y ) {
199
+ if (masm .isAVX ()) {
200
+ var ucomis = scalarSize == OperandSize .SS ? VUCOMISS : VUCOMISD ;
201
+ ucomis .encoding (encoding ).emit (masm , XMM , x , y );
202
+ } else {
203
+ UCOMIS .emit (masm , packedSize , x , y );
204
+ }
163
205
}
164
206
}
0 commit comments