You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Under certain circumstances, the existing rules for bit operations can
produce code that writes beyond its intended bounds. For example,
consider the following code:
func repro(b []byte, addr, bit int32) {
_ = b[3]
v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 | 1<<(bit&31)
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
}
Roughly speaking:
1. The expression `1 << (bit & 31)` is rewritten into `(SHLL 1 bit)`
2. The expression `uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 |
uint32(b[3])<<24` is rewritten into `(MOVLload &b[0])`
3. The statements `b[0] = byte(v) ... b[3] = byte(v >> 24)` are
rewritten into `(MOVLstore &b[0], v)`
4. `(ORL (SHLL 1, bit) (MOVLload &b[0]))` is rewritten into
`(BTSL (MOVLload &b[0]) bit)`. This is a valid transformation because
the destination is a register: in this case, the bit offset is masked
by the number of bits in the destination register. This is identical
to the masking performed by `SHL`.
5. `(MOVLstore &b[0] (BTSL (MOVLload &b[0]) bit))` is rewritten into
`(BTSLmodify &b[0] bit)`. This is an invalid transformation because
the destination is memory: in this case, the bit offset is not
masked, and the chosen instruction may write outside its intended
32-bit location.
These changes fix the invalid rewrite performed in step (5) by adding an
`AuxInt` field to `S(H|A)(L|R)(L|Q)` and `BT(S|R|C)(L|Q)` that indicates
whether or not the shift amount/bit offset is known to be bounded and
using that field to decide whether or not the bit offset operand to
`BT(S|R|C)(L|Q)modify` must be masked. Applying the adjusted rules to
the example above changes the rewrite performed in step (5) s.t. it
produces `(BTSLmodify &b[0] (ANDLconst [31] bit))`.
These changes also add several new rules to rewrite bit sets, toggles,
and clears that are rooted at `(OR|XOR|AND)(L|Q)modify` operators into
appropriate `BT(S|R|C)(L|Q)modify` operators. These rules catch cases
where `MOV(L|Q)store ((OR|XOR|AND)(L|Q) ...)` is rewritten to
`(OR|XOR|AND)(L|Q)modify` before the `(OR|XOR|AND)(L|Q) ...` can be
rewritten to `BT(S|R|C)(L|Q) ...`.
Overall, compilecmp reports small improvements in code size on
darwin/amd64 when the changes to the compiler itself are exlcuded:
file before after Δ %
math.s 35557 35563 +6 +0.017%
runtime.s 536464 536413 -51 -0.010%
bytes.s 32629 32593 -36 -0.110%
strings.s 44565 44529 -36 -0.081%
cmd/vendor/golang.org/x/sys/unix.s 81686 81678 -8 -0.010%
os/signal.s 7967 7959 -8 -0.100%
math/big.s 188235 188253 +18 +0.010%
cmd/link/internal/loader.s 89295 89056 -239 -0.268%
cmd/link/internal/ld.s 633551 633232 -319 -0.050%
cmd/link/internal/arm.s 18934 18928 -6 -0.032%
cmd/link/internal/arm64.s 31814 31801 -13 -0.041%
cmd/link/internal/riscv64.s 7347 7345 -2 -0.027%
cmd/compile/internal/ssa.s 4029173 4041251 +12078 +0.300%
total 21298280 21309664 +11384 +0.053%
Copy file name to clipboardExpand all lines: src/cmd/compile/internal/ssa/gen/AMD64.rules
+53-21Lines changed: 53 additions & 21 deletions
Original file line number
Diff line number
Diff line change
@@ -169,20 +169,20 @@
169
169
(Lsh16x(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMP(Q|L|W|B)const y [32])))
170
170
(Lsh8x(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMP(Q|L|W|B)const y [32])))
171
171
172
-
(Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLQ x y)
173
-
(Lsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL x y)
174
-
(Lsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL x y)
175
-
(Lsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL x y)
172
+
(Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLQ [true] x y)
173
+
(Lsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL [true] x y)
174
+
(Lsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL [true] x y)
175
+
(Lsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SHLL [true] x y)
176
176
177
177
(Rsh64Ux(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMP(Q|L|W|B)const y [64])))
178
178
(Rsh32Ux(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMP(Q|L|W|B)const y [32])))
179
179
(Rsh16Ux(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMP(Q|L|W|B)const y [16])))
180
180
(Rsh8Ux(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMP(Q|L|W|B)const y [8])))
181
181
182
-
(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRQ x y)
183
-
(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRL x y)
184
-
(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRW x y)
185
-
(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRB x y)
182
+
(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRQ [true] x y)
183
+
(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRL [true] x y)
184
+
(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRW [true] x y)
185
+
(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SHRB [true] x y)
186
186
187
187
// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
188
188
// We implement this by setting the shift value to -1 (all ones) if the shift value is >= width.
@@ -191,10 +191,10 @@
191
191
(Rsh16x(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (SARW <t> x (OR(Q|L|L|L) <y.Type> y (NOT(Q|L|L|L) <y.Type> (SBB(Q|L|L|L)carrymask <y.Type> (CMP(Q|L|W|B)const y [16])))))
192
192
(Rsh8x(64|32|16|8) <t> x y) && !shiftIsBounded(v) => (SARB <t> x (OR(Q|L|L|L) <y.Type> y (NOT(Q|L|L|L) <y.Type> (SBB(Q|L|L|L)carrymask <y.Type> (CMP(Q|L|W|B)const y [8])))))
193
193
194
-
(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SARQ x y)
195
-
(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SARL x y)
196
-
(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SARW x y)
197
-
(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SARB x y)
194
+
(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SARQ [true] x y)
195
+
(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SARL [true] x y)
196
+
(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SARW [true] x y)
197
+
(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SARB [true] x y)
198
198
199
199
// Lowering integer comparisons
200
200
(Less(64|32|16|8) x y) => (SETL (CMP(Q|L|W|B) x y))
0 commit comments