/
unary.go
126 lines (108 loc) · 3.43 KB
/
unary.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86
import (
"github.com/tsavola/wag/internal/gen"
"github.com/tsavola/wag/internal/gen/condition"
"github.com/tsavola/wag/internal/gen/operand"
"github.com/tsavola/wag/internal/gen/reg"
"github.com/tsavola/wag/internal/gen/rodata"
"github.com/tsavola/wag/internal/isa/prop"
"github.com/tsavola/wag/internal/isa/x86/in"
"github.com/tsavola/wag/wa"
)
func (MacroAssembler) Unary(f *gen.Func, props uint16, x operand.O) operand.O {
switch uint8(props) {
case prop.IntEqz:
r, _ := getScratchReg(f, x)
in.TEST.RegReg(&f.Text, x.Type, r, r)
f.Regs.Free(x.Type, r)
return operand.Flags(condition.Eq)
case prop.IntClz:
r, _ := allocResultReg(f, x)
if haveLZCNT() {
in.LZCNT.RegReg(&f.Text, x.Type, r, r)
} else {
in.BSR.RegReg(&f.Text, x.Type, RegScratch, r)
in.MOVi.RegImm32(&f.Text, x.Type, r, -1)
in.CMOVE.RegReg(&f.Text, x.Type, RegScratch, r)
in.MOVi.RegImm32(&f.Text, x.Type, r, (int32(x.Size())<<3)-1)
in.SUB.RegReg(&f.Text, x.Type, r, RegScratch)
}
return operand.Reg(x.Type, r)
case prop.IntCtz:
r, _ := allocResultReg(f, x)
if haveTZCNT() {
in.TZCNT.RegReg(&f.Text, x.Type, r, r)
} else {
in.BSF.RegReg(&f.Text, x.Type, r, r)
in.MOVi.RegImm32(&f.Text, x.Type, RegScratch, int32(x.Size())<<3)
in.CMOVE.RegReg(&f.Text, x.Type, r, RegScratch)
}
return operand.Reg(x.Type, r)
case prop.IntPopcnt:
var r reg.R
if havePOPCNT() {
r, _ = allocResultReg(f, x)
in.POPCNT.RegReg(&f.Text, x.Type, r, r)
} else {
r = popcnt(f, x)
}
return operand.Reg(x.Type, r)
case prop.FloatAbs:
r, _ := allocResultReg(f, x)
absFloatReg(&f.Prog, x.Type, r)
return operand.Reg(x.Type, r)
case prop.FloatNeg:
r, _ := allocResultReg(f, x)
negFloatReg(&f.Prog, x.Type, r)
return operand.Reg(x.Type, r)
case prop.FloatRoundOp:
r, _ := allocResultReg(f, x)
roundMode := int8(props >> 8)
in.ROUNDSx.RegRegImm8(&f.Text, x.Type, r, r, roundMode)
return operand.Reg(x.Type, r)
default: // FloatSqrt
r, _ := allocResultReg(f, x)
in.SQRTSx.RegReg(&f.Text, x.Type, r, r)
return operand.Reg(x.Type, r)
}
}
// absFloatReg in-place.
func absFloatReg(p *gen.Prog, t wa.Type, r reg.R) {
absMaskAddr := rodata.MaskAddr(rodata.Mask7fBase, t)
in.ANDPx.RegMemDisp(&p.Text, t, r, in.BaseText, absMaskAddr)
}
// negFloatReg in-place.
func negFloatReg(p *gen.Prog, t wa.Type, r reg.R) {
signMaskAddr := rodata.MaskAddr(rodata.Mask80Base, t)
in.XORPx.RegMemDisp(&p.Text, t, r, in.BaseText, signMaskAddr)
}
// Population count algorithm:
//
// func popcnt(x uint) (count int) {
// for count = 0; x != 0; count++ {
// x &= x - 1
// }
// return
// }
//
func popcnt(f *gen.Func, x operand.O) (count reg.R) {
count = f.Regs.AllocResult(x.Type)
pop, _ := getScratchReg(f, x)
temp := RegZero // cleared again at the end
in.XOR.RegReg(&f.Text, wa.I32, count, count)
in.TEST.RegReg(&f.Text, x.Type, pop, pop)
skipJump := in.JEcb.Stub8(&f.Text)
loopAddr := f.Text.Addr
in.INC.Reg(&f.Text, wa.I32, count)
in.MOV.RegReg(&f.Text, x.Type, temp, pop)
in.DEC.Reg(&f.Text, x.Type, temp)
in.AND.RegReg(&f.Text, x.Type, pop, temp)
in.JNEcb.Addr8(&f.Text, loopAddr)
linker.UpdateNearBranch(f.Text.Bytes(), skipJump)
in.XOR.RegReg(&f.Text, wa.I32, RegZero, RegZero) // temp reg
f.Regs.Free(x.Type, pop)
return
}