Compare commits

...
7 Commits
Author SHA1 Message Date
xtaci 2e5215a35d add multiport support on server 2022-10-08 18:03:33 +08:00
xtaci b9cb84efdf make parsing generic 2022-10-08 17:47:14 +08:00
xtaci f7a642525c upd test 2022-10-08 17:30:11 +08:00
xtaci 51e6fed5fc adding support for multiport dial 2022-10-08 13:02:18 +08:00
itheweiandGitHub c5232c2953 config --crypt null (#878) 2022-06-28 10:45:23 +08:00
xtaci 0a9243f27c fix missing vendor 2022-06-28 10:28:49 +08:00
xtaci b7167b5859 upd deps 2022-06-27 20:12:18 +08:00
689 changed files with 105858 additions and 122738 deletions
+20 -3
View File
@@ -1,18 +1,35 @@
package main
import (
"fmt"
"github.com/pkg/errors"
kcp "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/kcptun/generic"
"github.com/xtaci/tcpraw"
)
var dialCount uint64
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
defer func() {
dialCount++
}()
mp, err := generic.ParseMultiPort(config.RemoteAddr)
if err != nil {
return nil, err
}
remoteAddr := fmt.Sprintf("%v:%v", mp.Host, uint64(mp.MinPort)+dialCount%uint64(mp.MaxPort-mp.MinPort+1))
if config.TCP {
conn, err := tcpraw.Dial("tcp", config.RemoteAddr)
conn, err := tcpraw.Dial("tcp", remoteAddr)
if err != nil {
return nil, errors.Wrap(err, "tcpraw.Dial()")
}
return kcp.NewConn(config.RemoteAddr, block, config.DataShard, config.ParityShard, conn)
return kcp.NewConn(remoteAddr, block, config.DataShard, config.ParityShard, conn)
}
return kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
return kcp.DialWithOptions(remoteAddr, block, config.DataShard, config.ParityShard)
}
+3 -1
View File
@@ -109,7 +109,7 @@ func main() {
cli.StringFlag{
Name: "crypt",
Value: "aes",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none, null",
},
cli.StringFlag{
Name: "mode",
@@ -340,6 +340,8 @@ func main() {
log.Println("key derivation done")
var block kcp.BlockCrypt
switch config.Crypt {
case "null":
block = nil
case "sm4":
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
+50
View File
@@ -0,0 +1,50 @@
package generic
import (
"regexp"
"strconv"
"github.com/pkg/errors"
)
type MultiPort struct {
Host string
MinPort uint64
MaxPort uint64
}
// Parse mulitport listener or dialer
func ParseMultiPort(addr string) (*MultiPort, error) {
remoteAddrMatcher := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
matches := remoteAddrMatcher.FindStringSubmatch(addr)
if len(matches) >= 4 {
var minPort, maxPort int
minPort, err := strconv.Atoi(matches[2])
if err != nil {
return nil, err
}
maxPort = minPort
// multiport assignment
if matches[3] != "" {
maxPort, err = strconv.Atoi(matches[3])
if err != nil {
return nil, err
}
}
if (minPort > maxPort) || minPort > 65535 || maxPort > 65535 || minPort == 0 || maxPort == 0 {
return nil, errors.Errorf("invalid port range specified: minport:%v -> maxport %v", minPort, maxPort)
}
mp := new(MultiPort)
mp.Host = matches[1]
mp.MinPort = uint64(minPort)
mp.MaxPort = uint64(maxPort)
return mp, nil
}
return nil, errors.Errorf("malformed address:%v", addr)
}
+51
View File
@@ -0,0 +1,51 @@
package generic
import (
"fmt"
"regexp"
"strconv"
"testing"
)
func TestDial(t *testing.T) {
reg := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
matches := reg.FindStringSubmatch("www.unknown.unknown:20000-21000")
for i := 0; i < len(matches); i++ {
fmt.Println(matches[i])
}
minPort, err := strconv.Atoi(matches[2])
if err != nil {
t.Fatal(err)
}
maxPort, err := strconv.Atoi(matches[3])
if err != nil {
t.Fatal(err)
}
t.Log("minport:", minPort)
t.Log("maxport:", maxPort)
remoteAddr := fmt.Sprintf("%v:%v", matches[1], uint64(minPort)+1000%uint64(maxPort-minPort+1))
t.Log("RemoteAddr:", remoteAddr)
testcase2 := "1.2.3.4:20000"
matches = reg.FindStringSubmatch(testcase2)
for i := 0; i < len(matches); i++ {
t.Log(testcase2, "submatch", i, matches[i])
}
testcase3 := ":20000-20001"
matches = reg.FindStringSubmatch(testcase3)
for i := 0; i < len(matches); i++ {
t.Log(testcase3, "submatch", i, matches[i])
}
testcase4 := ":20000"
matches = reg.FindStringSubmatch(testcase4)
for i := 0; i < len(matches); i++ {
t.Log(testcase4, "submatch", i, matches[i])
}
}
+5 -1
View File
@@ -4,12 +4,16 @@ require (
github.com/coreos/go-iptables v0.4.2 // indirect
github.com/golang/snappy v0.0.1
github.com/google/gopacket v1.1.17 // indirect
github.com/klauspost/reedsolomon v1.10.0 // indirect
github.com/pkg/errors v0.9.1
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/urfave/cli v1.21.0
github.com/xtaci/kcp-go/v5 v5.6.1
github.com/xtaci/smux v1.5.16
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
)
go 1.14
+86 -20
View File
@@ -1,47 +1,65 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8=
github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
github.com/klauspost/reedsolomon v1.10.0 h1:MonMtg979rxSHjwtsla5dZLhreS0Lu42AyQ20bhjIGg=
github.com/klauspost/reedsolomon v1.10.0/go.mod h1:qHMIzMkuZUWqIh8mS/GruPdo3u0qwX2jk/LH440ON7Y=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92 h1:a9X2tFWnJR0JjYSz2n4PdmTGtDHtlnU7VI5bNjUDsyI=
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547 h1:SFJWLwOnjTJCt7JKf2fYH0RWtWJTkvYKIEfLvjJfOqM=
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/xtaci/kcp-go/v5 v5.5.17 h1:bkdaqtER0PMlP05BBHfu6W+71kt/NwbAk93KH7F78Ck=
github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/xtaci/smux v1.5.14 h1:1j+zJYDZRv9FHaWqCJfH5RPizIm0fSzJIFbfVn8zsfg=
github.com/xtaci/smux v1.5.14/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/smux v1.5.15 h1:6hMiXswcleXj5oNfcJc+DXS8Vj36XX2LaX98udog6Kc=
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
@@ -53,37 +71,85 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 h1:4JSJPND/+4555t1HfXYF4UEqDqiSKCgeV0+hbA8hMs4=
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+29 -14
View File
@@ -149,7 +149,7 @@ func main() {
cli.StringFlag{
Name: "crypt",
Value: "aes",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none, null",
},
cli.StringFlag{
Name: "mode",
@@ -361,6 +361,8 @@ func main() {
log.Println("key derivation done")
var block kcp.BlockCrypt
switch config.Crypt {
case "null":
block = nil
case "sm4":
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
@@ -430,22 +432,35 @@ func main() {
}
}
if config.TCP { // tcp dual stack
if conn, err := tcpraw.Listen("tcp", config.Listen); err == nil {
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
checkError(err)
wg.Add(1)
go loop(lis)
} else {
log.Println(err)
mp, err := generic.ParseMultiPort(config.Listen)
if err != nil {
log.Println(err)
return err
}
// create multiple listener
for port := mp.MinPort; port <= mp.MaxPort; port++ {
listenAddr := fmt.Sprintf("%v:%v", mp.Host, port)
if config.TCP { // tcp dual stack
if conn, err := tcpraw.Listen("tcp", listenAddr); err == nil {
log.Printf("Listening on: %v/tcp", listenAddr)
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
checkError(err)
wg.Add(1)
go loop(lis)
} else {
log.Println(err)
}
}
// udp stack
log.Printf("Listening on: %v/udp", listenAddr)
lis, err := kcp.ListenWithOptions(listenAddr, block, config.DataShard, config.ParityShard)
checkError(err)
wg.Add(1)
go loop(lis)
}
// udp stack
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
checkError(err)
wg.Add(1)
go loop(lis)
wg.Wait()
return nil
}
-46
View File
@@ -1,46 +0,0 @@
language: go
os:
- linux
- osx
- windows
arch:
- amd64
- arm64
go:
- 1.12.x
- 1.13.x
- 1.14.x
- master
script:
- go vet ./...
- go test -race ./...
- go test -tags=noasm ./...
stages:
- gofmt
- test
matrix:
allow_failures:
- go: 'master'
fast_finish: true
include:
- stage: gofmt
go: 1.14.x
os: linux
arch: amd64
script:
- diff <(gofmt -d .) <(printf "")
- diff <(gofmt -d ./private) <(printf "")
- go install github.com/klauspost/asmfmt/cmd/asmfmt
- diff <(asmfmt -d .) <(printf "")
- stage: i386
go: 1.14.x
os: linux
arch: amd64
script:
- GOOS=linux GOARCH=386 go test .
-191
View File
@@ -1,191 +0,0 @@
# cpuid
Package cpuid provides information about the CPU running the current program.
CPU features are detected on startup, and kept for fast access through the life of the application.
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
Package home: https://github.com/klauspost/cpuid
[![GoDoc][1]][2] [![Build Status][3]][4]
[1]: https://godoc.org/github.com/klauspost/cpuid?status.svg
[2]: https://godoc.org/github.com/klauspost/cpuid
[3]: https://travis-ci.org/klauspost/cpuid.svg?branch=master
[4]: https://travis-ci.org/klauspost/cpuid
# features
## x86 CPU Instructions
* **CMOV** (i686 CMOV)
* **NX** (NX (No-Execute) bit)
* **AMD3DNOW** (AMD 3DNOW)
* **AMD3DNOWEXT** (AMD 3DNowExt)
* **MMX** (standard MMX)
* **MMXEXT** (SSE integer functions or AMD MMX ext)
* **SSE** (SSE functions)
* **SSE2** (P4 SSE functions)
* **SSE3** (Prescott SSE3 functions)
* **SSSE3** (Conroe SSSE3 functions)
* **SSE4** (Penryn SSE4.1 functions)
* **SSE4A** (AMD Barcelona microarchitecture SSE4a instructions)
* **SSE42** (Nehalem SSE4.2 functions)
* **AVX** (AVX functions)
* **AVX2** (AVX2 functions)
* **FMA3** (Intel FMA 3)
* **FMA4** (Bulldozer FMA4 functions)
* **XOP** (Bulldozer XOP functions)
* **F16C** (Half-precision floating-point conversion)
* **BMI1** (Bit Manipulation Instruction Set 1)
* **BMI2** (Bit Manipulation Instruction Set 2)
* **TBM** (AMD Trailing Bit Manipulation)
* **LZCNT** (LZCNT instruction)
* **POPCNT** (POPCNT instruction)
* **AESNI** (Advanced Encryption Standard New Instructions)
* **CLMUL** (Carry-less Multiplication)
* **HTT** (Hyperthreading (enabled))
* **HLE** (Hardware Lock Elision)
* **RTM** (Restricted Transactional Memory)
* **RDRAND** (RDRAND instruction is available)
* **RDSEED** (RDSEED instruction is available)
* **ADX** (Intel ADX (Multi-Precision Add-Carry Instruction Extensions))
* **SHA** (Intel SHA Extensions)
* **AVX512F** (AVX-512 Foundation)
* **AVX512DQ** (AVX-512 Doubleword and Quadword Instructions)
* **AVX512IFMA** (AVX-512 Integer Fused Multiply-Add Instructions)
* **AVX512PF** (AVX-512 Prefetch Instructions)
* **AVX512ER** (AVX-512 Exponential and Reciprocal Instructions)
* **AVX512CD** (AVX-512 Conflict Detection Instructions)
* **AVX512BW** (AVX-512 Byte and Word Instructions)
* **AVX512VL** (AVX-512 Vector Length Extensions)
* **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions)
* **AVX512VBMI2** (AVX-512 Vector Bit Manipulation Instructions, Version 2)
* **AVX512VNNI** (AVX-512 Vector Neural Network Instructions)
* **AVX512VPOPCNTDQ** (AVX-512 Vector Population Count Doubleword and Quadword)
* **GFNI** (Galois Field New Instructions)
* **VAES** (Vector AES)
* **AVX512BITALG** (AVX-512 Bit Algorithms)
* **VPCLMULQDQ** (Carry-Less Multiplication Quadword)
* **AVX512BF16** (AVX-512 BFLOAT16 Instructions)
* **AVX512VP2INTERSECT** (AVX-512 Intersect for D/Q)
* **MPX** (Intel MPX (Memory Protection Extensions))
* **ERMS** (Enhanced REP MOVSB/STOSB)
* **RDTSCP** (RDTSCP Instruction)
* **CX16** (CMPXCHG16B Instruction)
* **SGX** (Software Guard Extensions, with activation details)
* **VMX** (Virtual Machine Extensions)
## Performance
* **RDTSCP()** Returns current cycle count. Can be used for benchmarking.
* **SSE2SLOW** (SSE2 is supported, but usually not faster)
* **SSE3SLOW** (SSE3 is supported, but usually not faster)
* **ATOM** (Atom processor, some SSSE3 instructions are slower)
* **Cache line** (Probable size of a cache line).
* **L1, L2, L3 Cache size** on newer Intel/AMD CPUs.
## ARM CPU features
# ARM FEATURE DETECTION DISABLED!
See [#52](https://github.com/klauspost/cpuid/issues/52).
Currently only `arm64` platforms are implemented.
* **FP** Single-precision and double-precision floating point
* **ASIMD** Advanced SIMD
* **EVTSTRM** Generic timer
* **AES** AES instructions
* **PMULL** Polynomial Multiply instructions (PMULL/PMULL2)
* **SHA1** SHA-1 instructions (SHA1C, etc)
* **SHA2** SHA-2 instructions (SHA256H, etc)
* **CRC32** CRC32/CRC32C instructions
* **ATOMICS** Large System Extensions (LSE)
* **FPHP** Half-precision floating point
* **ASIMDHP** Advanced SIMD half-precision floating point
* **ARMCPUID** Some CPU ID registers readable at user-level
* **ASIMDRDM** Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH)
* **JSCVT** Javascript-style double->int convert (FJCVTZS)
* **FCMA** Floating point complex number addition and multiplication
* **LRCPC** Weaker release consistency (LDAPR, etc)
* **DCPOP** Data cache clean to Point of Persistence (DC CVAP)
* **SHA3** SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
* **SM3** SM3 instructions
* **SM4** SM4 instructions
* **ASIMDDP** SIMD Dot Product
* **SHA512** SHA512 instructions
* **SVE** Scalable Vector Extension
* **GPA** Generic Pointer Authentication
## Cpu Vendor/VM
* **Intel**
* **AMD**
* **VIA**
* **Transmeta**
* **NSC**
* **KVM** (Kernel-based Virtual Machine)
* **MSVM** (Microsoft Hyper-V or Windows Virtual PC)
* **VMware**
* **XenHVM**
* **Bhyve**
* **Hygon**
# installing
```go get github.com/klauspost/cpuid```
# example
```Go
package main
import (
"fmt"
"github.com/klauspost/cpuid"
)
func main() {
// Print basic CPU information:
fmt.Println("Name:", cpuid.CPU.BrandName)
fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", cpuid.CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", cpuid.CPU.LogicalCores)
fmt.Println("Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model)
fmt.Println("Features:", cpuid.CPU.Features)
fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine)
fmt.Println("L1 Data Cache:", cpuid.CPU.Cache.L1D, "bytes")
fmt.Println("L1 Instruction Cache:", cpuid.CPU.Cache.L1D, "bytes")
fmt.Println("L2 Cache:", cpuid.CPU.Cache.L2, "bytes")
fmt.Println("L3 Cache:", cpuid.CPU.Cache.L3, "bytes")
// Test if we have a specific feature:
if cpuid.CPU.SSE() {
fmt.Println("We have Streaming SIMD Extensions")
}
}
```
Sample output:
```
>go run main.go
Name: Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz
PhysicalCores: 2
ThreadsPerCore: 2
LogicalCores: 4
Family 6 Model: 42
Features: CMOV,MMX,MMXEXT,SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2,AVX,AESNI,CLMUL
Cacheline bytes: 64
We have Streaming SIMD Extensions
```
# private package
In the "private" folder you can find an autogenerated version of the library you can include in your own packages.
For this purpose all exports are removed, and functions and constants are lowercased.
This is not a recommended way of using the library, but provided for convenience, if it is difficult for you to use external packages.
# license
This code is published under an MIT license. See LICENSE file for more information.
-1504
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -1,3 +0,0 @@
module github.com/klauspost/cpuid
go 1.12
+74
View File
@@ -0,0 +1,74 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
builds:
-
id: "cpuid"
binary: cpuid
main: ./cmd/cpuid/main.go
env:
- CGO_ENABLED=0
flags:
- -ldflags=-s -w
goos:
- aix
- linux
- freebsd
- netbsd
- windows
- darwin
goarch:
- 386
- amd64
- arm64
goarm:
- 7
archives:
-
id: cpuid
name_template: "cpuid-{{ .Os }}_{{ .Arch }}_{{ .Version }}"
replacements:
aix: AIX
darwin: OSX
linux: Linux
windows: Windows
386: i386
amd64: x86_64
freebsd: FreeBSD
netbsd: NetBSD
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^doc:'
- '^docs:'
- '^test:'
- '^tests:'
- '^Update\sREADME.md'
nfpms:
-
file_name_template: "cpuid_package_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
vendor: Klaus Post
homepage: https://github.com/klauspost/cpuid
maintainer: Klaus Post <klauspost@gmail.com>
description: CPUID Tool
license: BSD 3-Clause
formats:
- deb
- rpm
replacements:
darwin: Darwin
linux: Linux
freebsd: FreeBSD
amd64: x86_64
+258
View File
@@ -0,0 +1,258 @@
# cpuid
Package cpuid provides information about the CPU running the current program.
CPU features are detected on startup, and kept for fast access through the life of the application.
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
Package home: https://github.com/klauspost/cpuid
[![PkgGoDev](https://pkg.go.dev/badge/github.com/klauspost/cpuid)](https://pkg.go.dev/github.com/klauspost/cpuid/v2)
[![Build Status][3]][4]
[3]: https://travis-ci.org/klauspost/cpuid.svg?branch=master
[4]: https://travis-ci.org/klauspost/cpuid
## installing
`go get -u github.com/klauspost/cpuid/v2` using modules.
Drop `v2` for others.
## example
```Go
package main
import (
"fmt"
"strings"
. "github.com/klauspost/cpuid/v2"
)
func main() {
// Print basic CPU information:
fmt.Println("Name:", CPU.BrandName)
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", CPU.LogicalCores)
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
fmt.Println("Cacheline bytes:", CPU.CacheLine)
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
fmt.Println("Frequency", CPU.Hz, "hz")
// Test if we have these specific features:
if CPU.Supports(SSE, SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
Sample output:
```
>go run main.go
Name: AMD Ryzen 9 3950X 16-Core Processor
PhysicalCores: 16
ThreadsPerCore: 2
LogicalCores: 32
Family 23 Model: 113 Vendor ID: AMD
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CMOV,CX16,F16C,FMA3,HTT,HYPERVISOR,LZCNT,MMX,MMXEXT,NX,POPCNT,RDRAND,RDSEED,RDTSCP,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3
Cacheline bytes: 64
L1 Data Cache: 32768 bytes
L1 Instruction Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
Frequency 0 hz
We have Streaming SIMD 2 Extensions
```
# usage
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
Note that for some cpu/os combinations some features will not be detected.
`amd64` has rather good support and should work reliably on all platforms.
Note that hypervisors may not pass through all CPU features.
## arm64 feature detection
Not all operating systems provide ARM features directly
and there is no safe way to do so for the rest.
Currently `arm64/linux` and `arm64/freebsd` should be quite reliable.
`arm64/darwin` adds features expected from the M1 processor, but a lot remains undetected.
A `DetectARM()` can be used if you are able to control your deployment,
it will detect CPU features, but may crash if the OS doesn't intercept the calls.
A `-cpu.arm` flag for detecting unsafe ARM features can be added. See below.
Note that currently only features are detected on ARM,
no additional information is currently available.
## flags
It is possible to add flags that affects cpu detection.
For this the `Flags()` command is provided.
This must be called *before* `flag.Parse()` AND after the flags have been parsed `Detect()` must be called.
This means that any detection used in `init()` functions will not contain these flags.
Example:
```Go
package main
import (
"flag"
"fmt"
"strings"
"github.com/klauspost/cpuid/v2"
)
func main() {
cpuid.Flags()
flag.Parse()
cpuid.Detect()
// Test if we have these specific features:
if cpuid.CPU.Supports(cpuid.SSE, cpuid.SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
## commandline
Download as binary from: https://github.com/klauspost/cpuid/releases
Install from source:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
### Example
```
λ cpuid
Name: AMD Ryzen 9 3950X 16-Core Processor
Vendor String: AuthenticAMD
Vendor ID: AMD
PhysicalCores: 16
Threads Per Core: 2
Logical Cores: 32
CPU Family 23 Model: 113
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
Microarchitecture level: 3
Cacheline bytes: 64
L1 Instruction Cache: 32768 bytes
L1 Data Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
```
### JSON Output:
```
λ cpuid --json
{
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
"VendorID": 2,
"VendorString": "AuthenticAMD",
"PhysicalCores": 16,
"ThreadsPerCore": 2,
"LogicalCores": 32,
"Family": 23,
"Model": 113,
"CacheLine": 64,
"Hz": 0,
"BoostFreq": 0,
"Cache": {
"L1I": 32768,
"L1D": 32768,
"L2": 524288,
"L3": 16777216
},
"SGX": {
"Available": false,
"LaunchControl": false,
"SGX1Supported": false,
"SGX2Supported": false,
"MaxEnclaveSizeNot64": 0,
"MaxEnclaveSize64": 0,
"EPCSections": null
},
"Features": [
"ADX",
"AESNI",
"AVX",
"AVX2",
"BMI1",
"BMI2",
"CLMUL",
"CLZERO",
"CMOV",
"CMPXCHG8",
"CPBOOST",
"CX16",
"F16C",
"FMA3",
"FXSR",
"FXSROPT",
"HTT",
"HYPERVISOR",
"LAHF",
"LZCNT",
"MCAOVERFLOW",
"MMX",
"MMXEXT",
"MOVBE",
"NX",
"OSXSAVE",
"POPCNT",
"RDRAND",
"RDSEED",
"RDTSCP",
"SCE",
"SHA",
"SSE",
"SSE2",
"SSE3",
"SSE4",
"SSE42",
"SSE4A",
"SSSE3",
"SUCCOR",
"X87",
"XSAVE"
],
"X64Level": 3
}
```
### Check CPU microarch level
```
λ cpuid --check-level=3
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
Exit Code 0
λ cpuid --check-level=4
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
Exit Code 1
```
# license
This code is published under an MIT license. See LICENSE file for more information.
+1218
View File
File diff suppressed because it is too large Load Diff
@@ -40,3 +40,8 @@ TEXT ·asmRdtscpAsm(SB), 7, $0
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0
MOVL $0, eax+0(FP)
RET
@@ -40,3 +40,33 @@ TEXT ·asmRdtscpAsm(SB), 7, $0
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// From https://go-review.googlesource.com/c/sys/+/285572/
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0-1
MOVB $0, ret+0(FP) // default to false
#ifdef GOOS_darwin // return if not darwin
#ifdef GOARCH_amd64 // return if not amd64
// These values from:
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
#define commpage64_base_address 0x00007fffffe00000
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
#define commpage64_version (commpage64_base_address+0x01E)
#define hasAVX512F 0x0000004000000000
MOVQ $commpage64_version, BX
MOVW (BX), AX
CMPW AX, $13 // versions < 13 do not support AVX512
JL no_avx512
MOVQ $commpage64_cpu_capabilities64, BX
MOVQ (BX), AX
MOVQ $hasAVX512F, CX
ANDQ CX, AX
JZ no_avx512
MOVB $1, ret+0(FP)
no_avx512:
#endif
#endif
RET
@@ -1,6 +1,6 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo
//+build arm64,!gccgo,!noasm,!appengine
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
@@ -1,9 +1,12 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo,!noasm,!appengine
//go:build arm64 && !gccgo && !noasm && !appengine
// +build arm64,!gccgo,!noasm,!appengine
package cpuid
import "runtime"
func getMidr() (midr uint64)
func getProcFeatures() (procFeatures uint64)
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
@@ -15,14 +18,19 @@ func initCPU() {
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(c *CPUInfo) {
// ARM64 disabled for now.
if true {
func addInfo(c *CPUInfo, safe bool) {
// Seems to be safe to assume on ARM64
c.CacheLine = 64
detectOS(c)
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
if safe && !c.Supports(ARMCPUID) && runtime.GOOS != "freebsd" {
return
}
// midr := getMidr()
midr := getMidr()
// MIDR_EL1 - Main ID Register
// https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/midr_el1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
@@ -37,11 +45,70 @@ func addInfo(c *CPUInfo) {
// | Revision | [3-0] | y |
// x--------------------------------------------------x
// fmt.Printf(" implementer: 0x%02x\n", (midr>>24)&0xff)
// fmt.Printf(" variant: 0x%01x\n", (midr>>20)&0xf)
// fmt.Printf("architecture: 0x%01x\n", (midr>>16)&0xf)
// fmt.Printf(" part num: 0x%03x\n", (midr>>4)&0xfff)
// fmt.Printf(" revision: 0x%01x\n", (midr>>0)&0xf)
switch (midr >> 24) & 0xff {
case 0xC0:
c.VendorString = "Ampere Computing"
c.VendorID = Ampere
case 0x41:
c.VendorString = "Arm Limited"
c.VendorID = ARM
case 0x42:
c.VendorString = "Broadcom Corporation"
c.VendorID = Broadcom
case 0x43:
c.VendorString = "Cavium Inc"
c.VendorID = Cavium
case 0x44:
c.VendorString = "Digital Equipment Corporation"
c.VendorID = DEC
case 0x46:
c.VendorString = "Fujitsu Ltd"
c.VendorID = Fujitsu
case 0x49:
c.VendorString = "Infineon Technologies AG"
c.VendorID = Infineon
case 0x4D:
c.VendorString = "Motorola or Freescale Semiconductor Inc"
c.VendorID = Motorola
case 0x4E:
c.VendorString = "NVIDIA Corporation"
c.VendorID = NVIDIA
case 0x50:
c.VendorString = "Applied Micro Circuits Corporation"
c.VendorID = AMCC
case 0x51:
c.VendorString = "Qualcomm Inc"
c.VendorID = Qualcomm
case 0x56:
c.VendorString = "Marvell International Ltd"
c.VendorID = Marvell
case 0x69:
c.VendorString = "Intel Corporation"
c.VendorID = Intel
}
// Lower 4 bits: Architecture
// Architecture Meaning
// 0b0001 Armv4.
// 0b0010 Armv4T.
// 0b0011 Armv5 (obsolete).
// 0b0100 Armv5T.
// 0b0101 Armv5TE.
// 0b0110 Armv5TEJ.
// 0b0111 Armv6.
// 0b1111 Architectural features are individually identified in the ID_* registers, see 'ID registers'.
// Upper 4 bit: Variant
// An IMPLEMENTATION DEFINED variant number.
// Typically, this field is used to distinguish between different product variants, or major revisions of a product.
c.Family = int(midr>>16) & 0xff
// PartNum, bits [15:4]
// An IMPLEMENTATION DEFINED primary part number for the device.
// On processors implemented by Arm, if the top four bits of the primary
// part number are 0x0 or 0x7, the variant and architecture are encoded differently.
// Revision, bits [3:0]
// An IMPLEMENTATION DEFINED revision number for the device.
c.Model = int(midr) & 0xffff
procFeatures := getProcFeatures()
@@ -68,25 +135,18 @@ func addInfo(c *CPUInfo) {
// | EL0 | [3-0] | n |
// x--------------------------------------------------x
var f ArmFlags
var f flagSet
// if procFeatures&(0xf<<48) != 0 {
// fmt.Println("DIT")
// }
if procFeatures&(0xf<<32) != 0 {
f |= SVE
}
f.setIf(procFeatures&(0xf<<32) != 0, SVE)
if procFeatures&(0xf<<20) != 15<<20 {
f |= ASIMD
if procFeatures&(0xf<<20) == 1<<20 {
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
f |= FPHP
f |= ASIMDHP
}
}
if procFeatures&(0xf<<16) != 0 {
f |= FP
f.set(ASIMD)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
f.setIf(procFeatures&(0xf<<20) == 1<<20, FPHP, ASIMDHP)
}
f.setIf(procFeatures&(0xf<<16) != 0, FP)
instAttrReg0, instAttrReg1 := getInstAttributes()
@@ -127,46 +187,22 @@ func addInfo(c *CPUInfo) {
// if instAttrReg0&(0xf<<48) != 0 {
// fmt.Println("FHM")
// }
if instAttrReg0&(0xf<<44) != 0 {
f |= ASIMDDP
}
if instAttrReg0&(0xf<<40) != 0 {
f |= SM4
}
if instAttrReg0&(0xf<<36) != 0 {
f |= SM3
}
if instAttrReg0&(0xf<<32) != 0 {
f |= SHA3
}
if instAttrReg0&(0xf<<28) != 0 {
f |= ASIMDRDM
}
if instAttrReg0&(0xf<<20) != 0 {
f |= ATOMICS
}
if instAttrReg0&(0xf<<16) != 0 {
f |= CRC32
}
if instAttrReg0&(0xf<<12) != 0 {
f |= SHA2
}
if instAttrReg0&(0xf<<12) == 2<<12 {
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
f |= SHA512
}
if instAttrReg0&(0xf<<8) != 0 {
f |= SHA1
}
if instAttrReg0&(0xf<<4) != 0 {
f |= AES
}
if instAttrReg0&(0xf<<4) == 2<<4 {
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
f |= PMULL
}
f.setIf(instAttrReg0&(0xf<<44) != 0, ASIMDDP)
f.setIf(instAttrReg0&(0xf<<40) != 0, SM4)
f.setIf(instAttrReg0&(0xf<<36) != 0, SM3)
f.setIf(instAttrReg0&(0xf<<32) != 0, SHA3)
f.setIf(instAttrReg0&(0xf<<28) != 0, ASIMDRDM)
f.setIf(instAttrReg0&(0xf<<20) != 0, ATOMICS)
f.setIf(instAttrReg0&(0xf<<16) != 0, CRC32)
f.setIf(instAttrReg0&(0xf<<12) != 0, SHA2)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
f.setIf(instAttrReg0&(0xf<<12) == 2<<12, SHA512)
f.setIf(instAttrReg0&(0xf<<8) != 0, SHA1)
f.setIf(instAttrReg0&(0xf<<4) != 0, AESARM)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
f.setIf(instAttrReg0&(0xf<<4) == 2<<4, PMULL)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
//
@@ -194,26 +230,18 @@ func addInfo(c *CPUInfo) {
// if instAttrReg1&(0xf<<28) != 0 {
// fmt.Println("GPI")
// }
if instAttrReg1&(0xf<<28) != 24 {
f |= GPA
}
if instAttrReg1&(0xf<<20) != 0 {
f |= LRCPC
}
if instAttrReg1&(0xf<<16) != 0 {
f |= FCMA
}
if instAttrReg1&(0xf<<12) != 0 {
f |= JSCVT
}
f.setIf(instAttrReg1&(0xf<<28) != 24, GPA)
f.setIf(instAttrReg1&(0xf<<20) != 0, LRCPC)
f.setIf(instAttrReg1&(0xf<<16) != 0, FCMA)
f.setIf(instAttrReg1&(0xf<<12) != 0, JSCVT)
// if instAttrReg1&(0xf<<8) != 0 {
// fmt.Println("API")
// }
// if instAttrReg1&(0xf<<4) != 0 {
// fmt.Println("APA")
// }
if instAttrReg1&(0xf<<0) != 0 {
f |= DCPOP
}
c.Arm = f
f.setIf(instAttrReg1&(0xf<<0) != 0, DCPOP)
// Store
c.featureSet.or(f)
}
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build !amd64,!386,!arm64 gccgo noasm appengine
//go:build (!amd64 && !386 && !arm64) || gccgo || noasm || appengine
// +build !amd64,!386,!arm64 gccgo noasm appengine
package cpuid
@@ -11,4 +12,4 @@ func initCPU() {
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(info *CPUInfo) {}
func addInfo(info *CPUInfo, safe bool) {}
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build 386,!gccgo,!noasm amd64,!gccgo,!noasm,!appengine
//go:build (386 && !gccgo && !noasm && !appengine) || (amd64 && !gccgo && !noasm && !appengine)
// +build 386,!gccgo,!noasm,!appengine amd64,!gccgo,!noasm,!appengine
package cpuid
@@ -8,26 +9,28 @@ func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
func asmXgetbv(index uint32) (eax, edx uint32)
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
func asmDarwinHasAVX512() bool
func initCPU() {
cpuid = asmCpuid
cpuidex = asmCpuidex
xgetbv = asmXgetbv
rdtscpAsm = asmRdtscpAsm
darwinHasAVX512 = asmDarwinHasAVX512
}
func addInfo(c *CPUInfo) {
func addInfo(c *CPUInfo, safe bool) {
c.maxFunc = maxFunctionID()
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model = familyModel()
c.Features = support()
c.SGX = hasSGX(c.Features&SGX != 0, c.Features&SGXLC != 0)
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.ThreadsPerCore = threadsPerCore()
c.LogicalCores = logicalCores()
c.PhysicalCores = physicalCores()
c.VendorID, c.VendorString = vendorID()
c.Hz = hertz(c.BrandName)
c.cacheSize()
c.frequencies()
}
+216
View File
@@ -0,0 +1,216 @@
// Code generated by "stringer -type=FeatureID,Vendor"; DO NOT EDIT.
package cpuid
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ADX-1]
_ = x[AESNI-2]
_ = x[AMD3DNOW-3]
_ = x[AMD3DNOWEXT-4]
_ = x[AMXBF16-5]
_ = x[AMXINT8-6]
_ = x[AMXTILE-7]
_ = x[AVX-8]
_ = x[AVX2-9]
_ = x[AVX512BF16-10]
_ = x[AVX512BITALG-11]
_ = x[AVX512BW-12]
_ = x[AVX512CD-13]
_ = x[AVX512DQ-14]
_ = x[AVX512ER-15]
_ = x[AVX512F-16]
_ = x[AVX512FP16-17]
_ = x[AVX512IFMA-18]
_ = x[AVX512PF-19]
_ = x[AVX512VBMI-20]
_ = x[AVX512VBMI2-21]
_ = x[AVX512VL-22]
_ = x[AVX512VNNI-23]
_ = x[AVX512VP2INTERSECT-24]
_ = x[AVX512VPOPCNTDQ-25]
_ = x[AVXSLOW-26]
_ = x[BMI1-27]
_ = x[BMI2-28]
_ = x[CETIBT-29]
_ = x[CETSS-30]
_ = x[CLDEMOTE-31]
_ = x[CLMUL-32]
_ = x[CLZERO-33]
_ = x[CMOV-34]
_ = x[CMPXCHG8-35]
_ = x[CPBOOST-36]
_ = x[CX16-37]
_ = x[ENQCMD-38]
_ = x[ERMS-39]
_ = x[F16C-40]
_ = x[FMA3-41]
_ = x[FMA4-42]
_ = x[FXSR-43]
_ = x[FXSROPT-44]
_ = x[GFNI-45]
_ = x[HLE-46]
_ = x[HTT-47]
_ = x[HWA-48]
_ = x[HYPERVISOR-49]
_ = x[IBPB-50]
_ = x[IBS-51]
_ = x[IBSBRNTRGT-52]
_ = x[IBSFETCHSAM-53]
_ = x[IBSFFV-54]
_ = x[IBSOPCNT-55]
_ = x[IBSOPCNTEXT-56]
_ = x[IBSOPSAM-57]
_ = x[IBSRDWROPCNT-58]
_ = x[IBSRIPINVALIDCHK-59]
_ = x[IBS_PREVENTHOST-60]
_ = x[INT_WBINVD-61]
_ = x[INVLPGB-62]
_ = x[LAHF-63]
_ = x[LZCNT-64]
_ = x[MCAOVERFLOW-65]
_ = x[MCOMMIT-66]
_ = x[MMX-67]
_ = x[MMXEXT-68]
_ = x[MOVBE-69]
_ = x[MOVDIR64B-70]
_ = x[MOVDIRI-71]
_ = x[MPX-72]
_ = x[MSR_PAGEFLUSH-73]
_ = x[MSRIRC-74]
_ = x[NX-75]
_ = x[OSXSAVE-76]
_ = x[PCONFIG-77]
_ = x[POPCNT-78]
_ = x[RDPRU-79]
_ = x[RDRAND-80]
_ = x[RDSEED-81]
_ = x[RDTSCP-82]
_ = x[RTM-83]
_ = x[RTM_ALWAYS_ABORT-84]
_ = x[SCE-85]
_ = x[SERIALIZE-86]
_ = x[SEV-87]
_ = x[SEV_64BIT-88]
_ = x[SEV_ALTERNATIVE-89]
_ = x[SEV_DEBUGSWAP-90]
_ = x[SEV_ES-91]
_ = x[SEV_RESTRICTED-92]
_ = x[SEV_SNP-93]
_ = x[SGX-94]
_ = x[SGXLC-95]
_ = x[SHA-96]
_ = x[SME-97]
_ = x[SME_COHERENT-98]
_ = x[SSE-99]
_ = x[SSE2-100]
_ = x[SSE3-101]
_ = x[SSE4-102]
_ = x[SSE42-103]
_ = x[SSE4A-104]
_ = x[SSSE3-105]
_ = x[STIBP-106]
_ = x[SUCCOR-107]
_ = x[TBM-108]
_ = x[TME-109]
_ = x[TSXLDTRK-110]
_ = x[VAES-111]
_ = x[VMPL-112]
_ = x[VMSA_REGPROT-113]
_ = x[VMX-114]
_ = x[VPCLMULQDQ-115]
_ = x[VTE-116]
_ = x[WAITPKG-117]
_ = x[WBNOINVD-118]
_ = x[X87-119]
_ = x[XGETBV1-120]
_ = x[XOP-121]
_ = x[XSAVE-122]
_ = x[XSAVEC-123]
_ = x[XSAVEOPT-124]
_ = x[XSAVES-125]
_ = x[AESARM-126]
_ = x[ARMCPUID-127]
_ = x[ASIMD-128]
_ = x[ASIMDDP-129]
_ = x[ASIMDHP-130]
_ = x[ASIMDRDM-131]
_ = x[ATOMICS-132]
_ = x[CRC32-133]
_ = x[DCPOP-134]
_ = x[EVTSTRM-135]
_ = x[FCMA-136]
_ = x[FP-137]
_ = x[FPHP-138]
_ = x[GPA-139]
_ = x[JSCVT-140]
_ = x[LRCPC-141]
_ = x[PMULL-142]
_ = x[SHA1-143]
_ = x[SHA2-144]
_ = x[SHA3-145]
_ = x[SHA512-146]
_ = x[SM3-147]
_ = x[SM4-148]
_ = x[SVE-149]
_ = x[lastID-150]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXSLOWBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPXCHG8CPBOOSTCX16ENQCMDERMSF16CFMA3FMA4FXSRFXSROPTGFNIHLEHTTHWAHYPERVISORIBPBIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_PREVENTHOSTINT_WBINVDINVLPGBLAHFLZCNTMCAOVERFLOWMCOMMITMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMPXMSR_PAGEFLUSHMSRIRCNXOSXSAVEPCONFIGPOPCNTRDPRURDRANDRDSEEDRDTSCPRTMRTM_ALWAYS_ABORTSCESERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSUCCORTBMTMETSXLDTRKVAESVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 58, 62, 72, 84, 92, 100, 108, 116, 123, 133, 143, 151, 161, 172, 180, 190, 208, 223, 230, 234, 238, 244, 249, 257, 262, 268, 272, 280, 287, 291, 297, 301, 305, 309, 313, 317, 324, 328, 331, 334, 337, 347, 351, 354, 364, 375, 381, 389, 400, 408, 420, 436, 451, 461, 468, 472, 477, 488, 495, 498, 504, 509, 518, 525, 528, 541, 547, 549, 556, 563, 569, 574, 580, 586, 592, 595, 611, 614, 623, 626, 635, 650, 663, 669, 683, 690, 693, 698, 701, 704, 716, 719, 723, 727, 731, 736, 741, 746, 751, 757, 760, 763, 771, 775, 779, 791, 794, 804, 807, 814, 822, 825, 832, 835, 840, 846, 854, 860, 866, 874, 879, 886, 893, 901, 908, 913, 918, 925, 929, 931, 935, 938, 943, 948, 953, 957, 961, 965, 971, 974, 977, 980, 986}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
return "FeatureID(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FeatureID_name[_FeatureID_index[i]:_FeatureID_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[VendorUnknown-0]
_ = x[Intel-1]
_ = x[AMD-2]
_ = x[VIA-3]
_ = x[Transmeta-4]
_ = x[NSC-5]
_ = x[KVM-6]
_ = x[MSVM-7]
_ = x[VMware-8]
_ = x[XenHVM-9]
_ = x[Bhyve-10]
_ = x[Hygon-11]
_ = x[SiS-12]
_ = x[RDC-13]
_ = x[Ampere-14]
_ = x[ARM-15]
_ = x[Broadcom-16]
_ = x[Cavium-17]
_ = x[DEC-18]
_ = x[Fujitsu-19]
_ = x[Infineon-20]
_ = x[Motorola-21]
_ = x[NVIDIA-22]
_ = x[AMCC-23]
_ = x[Qualcomm-24]
_ = x[Marvell-25]
_ = x[lastVendor-26]
}
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvelllastVendor"
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 155}
func (i Vendor) String() string {
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {
return "Vendor(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Vendor_name[_Vendor_index[i]:_Vendor_index[i+1]]
}
+3
View File
@@ -0,0 +1,3 @@
module github.com/klauspost/cpuid/v2
go 1.15
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
package cpuid
import "runtime"
func detectOS(c *CPUInfo) bool {
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
// to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64.
// TODO: Add more if we know them.
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return true
}
+130
View File
@@ -0,0 +1,130 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file located
// here https://github.com/golang/sys/blob/master/LICENSE
package cpuid
import (
"encoding/binary"
"io/ioutil"
"runtime"
)
// HWCAP bits.
const (
hwcap_FP = 1 << 0
hwcap_ASIMD = 1 << 1
hwcap_EVTSTRM = 1 << 2
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_FPHP = 1 << 9
hwcap_ASIMDHP = 1 << 10
hwcap_CPUID = 1 << 11
hwcap_ASIMDRDM = 1 << 12
hwcap_JSCVT = 1 << 13
hwcap_FCMA = 1 << 14
hwcap_LRCPC = 1 << 15
hwcap_DCPOP = 1 << 16
hwcap_SHA3 = 1 << 17
hwcap_SM3 = 1 << 18
hwcap_SM4 = 1 << 19
hwcap_ASIMDDP = 1 << 20
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
)
func detectOS(c *CPUInfo) bool {
// For now assuming no hyperthreading is reasonable.
c.LogicalCores = runtime.NumCPU()
c.PhysicalCores = c.LogicalCores
c.ThreadsPerCore = 1
if hwcap == 0 {
// We did not get values from the runtime.
// Try reading /proc/self/auxv
// From https://github.com/golang/sys
const (
_AT_HWCAP = 16
_AT_HWCAP2 = 26
uintSize = int(32 << (^uint(0) >> 63))
)
buf, err := ioutil.ReadFile("/proc/self/auxv")
if err != nil {
// e.g. on android /proc/self/auxv is not accessible, so silently
// ignore the error and leave Initialized = false. On some
// architectures (e.g. arm64) doinit() implements a fallback
// readout and will set Initialized = true again.
return false
}
bo := binary.LittleEndian
for len(buf) >= 2*(uintSize/8) {
var tag, val uint
switch uintSize {
case 32:
tag = uint(bo.Uint32(buf[0:]))
val = uint(bo.Uint32(buf[4:]))
buf = buf[8:]
case 64:
tag = uint(bo.Uint64(buf[0:]))
val = uint(bo.Uint64(buf[8:]))
buf = buf[16:]
}
switch tag {
case _AT_HWCAP:
hwcap = val
case _AT_HWCAP2:
// Not used
}
}
if hwcap == 0 {
return false
}
}
// HWCap was populated by the runtime from the auxiliary vector.
// Use HWCap information since reading aarch64 system registers
// is not supported in user space on older linux kernels.
c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
// The Samsung S9+ kernel reports support for atomics, but not all cores
// actually support them, resulting in SIGILL. See issue #28431.
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
return true
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
//go:build arm64 && !linux && !darwin
// +build arm64,!linux,!darwin
package cpuid
import "runtime"
func detectOS(c *CPUInfo) bool {
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return false
}
+8
View File
@@ -0,0 +1,8 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build nounsafe
// +build nounsafe
package cpuid
var hwcap uint
+11
View File
@@ -0,0 +1,11 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build !nounsafe
// +build !nounsafe
package cpuid
import _ "unsafe" // needed for go:linkname
//go:linkname hwcap internal/cpu.HWCap
var hwcap uint
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
set -e
go tool dist list | while IFS=/ read os arch; do
echo "Checking $os/$arch..."
echo " normal"
GOARCH=$arch GOOS=$os go build -o /dev/null .
echo " noasm"
GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null .
echo " appengine"
GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null .
echo " noasm,appengine"
GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null .
done
-77
View File
@@ -1,77 +0,0 @@
language: go
os:
- linux
- osx
- windows
arch:
- amd64
- arm64
- ppc64le
- s390x
go:
- 1.12.x
- 1.13.x
- 1.14.x
- master
install:
- go get ./...
script:
- go vet ./...
- go test -cpu=1,2 .
- go test -tags=noasm -cpu=1,2 .
- go build examples/simple-decoder.go
- go build examples/simple-encoder.go
- go build examples/stream-decoder.go
- go build examples/stream-encoder.go
stages:
- gofmt
- test
- deploy
jobs:
allow_failures:
- go: 'master'
- arch: s390x
fast_finish: true
include:
- stage: gofmt
go: 1.14.x
os: linux
arch: amd64
script:
- diff <(gofmt -d .) <(printf "")
- diff <(gofmt -d ./examples) <(printf "")
- go install github.com/klauspost/asmfmt/cmd/asmfmt
- diff <(asmfmt -d .) <(printf "")
- stage: race
go: 1.14.x
os: linux
arch: amd64
script:
- go test -cpu=1 -short -race .
- go test -cpu=2 -short -race .
- go test -tags=noasm -cpu=1 -short -race .
- go test -tags=noasm -cpu=4 -short -race .
- go test -no-avx512 -short -race .
- go test -no-avx512 -no-avx2 -short -race .
- go test -no-avx512 -no-avx2 -no-ssse3 -short -race .
- stage: amd64-noasm
go: 1.14.x
os: linux
arch: amd64
script:
- go test -no-avx512
- go test -no-avx512 -no-avx2
- go test -no-avx512 -no-avx2 -no-ssse3
- stage: i386
go: 1.14.x
os: linux
arch: amd64
script:
- GOOS=linux GOARCH=386 go test -short .
+76 -10
View File
@@ -1,8 +1,6 @@
# Reed-Solomon
[![GoDoc][1]][2] [![Build Status][3]][4]
[![Go Reference](https://pkg.go.dev/badge/github.com/klauspost/reedsolomon.svg)](https://pkg.go.dev/github.com/klauspost/reedsolomon) [![Build Status][3]][4]
[1]: https://godoc.org/github.com/klauspost/reedsolomon?status.svg
[2]: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
[3]: https://travis-ci.org/klauspost/reedsolomon.svg?branch=master
[4]: https://travis-ci.org/klauspost/reedsolomon
@@ -23,7 +21,19 @@ To get the package use the standard:
go get -u github.com/klauspost/reedsolomon
```
Using Go modules recommended.
# Changes
## 2021
* Use `GOAMD64=v4` to enable faster AVX2.
* Add progressive shard encoding.
* Wider AVX2 loops
* Limit concurrency on AVX2, since we are likely memory bound.
* Allow 0 parity shards.
* Allow disabling inversion cache.
* Faster AVX2 encoding.
## May 2020
@@ -178,6 +188,17 @@ If you are only interested in the data shards (for reading purposes) you can cal
err := enc.ReconstructData(data)
```
If you don't need all data shards you can use `ReconstructSome()`:
```Go
// Delete two data shards
data[3] = nil
data[7] = nil
// Reconstruct just the shard 3
err := enc.ReconstructSome(data, []bool{false, false, false, true, false, false, false, false})
```
So to sum up reconstruction:
* The number of data/parity shards must match the numbers used for encoding.
* The order of shards must be the same as used when encoding.
@@ -209,6 +230,49 @@ To join a data set, use the `Join()` function, which will join the shards and wr
err = enc.Join(io.Discard, data, len(bigfile))
```
# Progressive encoding
It is possible to encode individual shards using EncodeIdx:
```Go
// EncodeIdx will add parity for a single data shard.
// Parity shards should start out as 0. The caller must zero them.
// Data shards must be delivered exactly once. There is no check for this.
// The parity shards will always be updated and the data shards will remain the same.
EncodeIdx(dataShard []byte, idx int, parity [][]byte) error
```
This allows progressively encoding the parity by sending individual data shards.
There is no requirement on shards being delivered in order,
but when sent in order it allows encoding shards one at the time,
effectively allowing the operation to be streaming.
The result will be the same as encoding all shards at once.
There is a minor speed penalty using this method, so send
shards at once if they are available.
## Example
```Go
func test() {
// Create an encoder with 7 data and 3 parity slices.
enc, _ := reedsolomon.New(7, 3)
// This will be our output parity.
parity := make([][]byte, 3)
for i := range parity {
parity[i] = make([]byte, 10000)
}
for i := 0; i < 7; i++ {
// Send data shards one at the time.
_ = enc.EncodeIdx(make([]byte, 10000), i, parity)
}
// parity now contains parity, as if all data was sent in one call.
}
```
# Streaming/Merging
It might seem like a limitation that all data should be in memory,
@@ -300,14 +364,16 @@ Performance depends mainly on the number of parity shards.
In rough terms, doubling the number of parity shards will double the encoding time.
Here are the throughput numbers with some different selections of data and parity shards.
For reference each shard is 1MB random data, and 2 CPU cores are used for encoding.
For reference each shard is 1MB random data, and 16 CPU cores are used for encoding.
| Data | Parity | Parity | MB/s | SSSE3 MB/s | SSSE3 Speed | Rel. Speed |
|------|--------|--------|--------|-------------|-------------|------------|
| 5 | 2 | 40% | 576,11 | 2599,2 | 451% | 100,00% |
| 10 | 2 | 20% | 587,73 | 3100,28 | 528% | 102,02% |
| 10 | 4 | 40% | 298,38 | 2470,97 | 828% | 51,79% |
| 50 | 20 | 40% | 59,81 | 713,28 | 1193% | 10,38% |
| Data | Parity | Go MB/s | SSSE3 MB/s | AVX2 MB/s |
|------|--------|---------|------------|-----------|
| 5 | 2 | 14287 | 66355 | 108755 |
| 8 | 8 | 5569 | 34298 | 70516 |
| 10 | 4 | 6766 | 48237 | 93875 |
| 50 | 20 | 1540 | 12130 | 22090 |
The throughput numbers here is the size of the encoded data and parity shards.
If `runtime.GOMAXPROCS()` is set to a value higher than 1,
the encoder will use multiple goroutines to perform the calculations in `Verify`, `Encode` and `Reconstruct`.
-20
View File
@@ -1,20 +0,0 @@
os: Visual Studio 2015
platform: x64
clone_folder: c:\gopath\src\github.com\klauspost\reedsolomon
# environment variables
environment:
GOPATH: c:\gopath
install:
- echo %PATH%
- echo %GOPATH%
- go version
- go env
- go get -d ./...
build_script:
- go test -v -cpu=2 ./...
- go test -cpu=1,2,4 -short -race ./...
+9 -7
View File
@@ -76,7 +76,7 @@ func galSub(a, b byte) byte {
// Table from https://github.com/templexxx/reedsolomon
var invTable = [256]byte{0x0, 0x1, 0x8e, 0xf4, 0x47, 0xa7, 0x7a, 0xba, 0xad, 0x9d, 0xdd, 0x98, 0x3d, 0xaa, 0x5d, 0x96, 0xd8, 0x72, 0xc0, 0x58, 0xe0, 0x3e, 0x4c, 0x66, 0x90, 0xde, 0x55, 0x80, 0xa0, 0x83, 0x4b, 0x2a, 0x6c, 0xed, 0x39, 0x51, 0x60, 0x56, 0x2c, 0x8a, 0x70, 0xd0, 0x1f, 0x4a, 0x26, 0x8b, 0x33, 0x6e, 0x48, 0x89, 0x6f, 0x2e, 0xa4, 0xc3, 0x40, 0x5e, 0x50, 0x22, 0xcf, 0xa9, 0xab, 0xc, 0x15, 0xe1, 0x36, 0x5f, 0xf8, 0xd5, 0x92, 0x4e, 0xa6, 0x4, 0x30, 0x88, 0x2b, 0x1e, 0x16, 0x67, 0x45, 0x93, 0x38, 0x23, 0x68, 0x8c, 0x81, 0x1a, 0x25, 0x61, 0x13, 0xc1, 0xcb, 0x63, 0x97, 0xe, 0x37, 0x41, 0x24, 0x57, 0xca, 0x5b, 0xb9, 0xc4, 0x17, 0x4d, 0x52, 0x8d, 0xef, 0xb3, 0x20, 0xec, 0x2f, 0x32, 0x28, 0xd1, 0x11, 0xd9, 0xe9, 0xfb, 0xda, 0x79, 0xdb, 0x77, 0x6, 0xbb, 0x84, 0xcd, 0xfe, 0xfc, 0x1b, 0x54, 0xa1, 0x1d, 0x7c, 0xcc, 0xe4, 0xb0, 0x49, 0x31, 0x27, 0x2d, 0x53, 0x69, 0x2, 0xf5, 0x18, 0xdf, 0x44, 0x4f, 0x9b, 0xbc, 0xf, 0x5c, 0xb, 0xdc, 0xbd, 0x94, 0xac, 0x9, 0xc7, 0xa2, 0x1c, 0x82, 0x9f, 0xc6, 0x34, 0xc2, 0x46, 0x5, 0xce, 0x3b, 0xd, 0x3c, 0x9c, 0x8, 0xbe, 0xb7, 0x87, 0xe5, 0xee, 0x6b, 0xeb, 0xf2, 0xbf, 0xaf, 0xc5, 0x64, 0x7, 0x7b, 0x95, 0x9a, 0xae, 0xb6, 0x12, 0x59, 0xa5, 0x35, 0x65, 0xb8, 0xa3, 0x9e, 0xd2, 0xf7, 0x62, 0x5a, 0x85, 0x7d, 0xa8, 0x3a, 0x29, 0x71, 0xc8, 0xf6, 0xf9, 0x43, 0xd7, 0xd6, 0x10, 0x73, 0x76, 0x78, 0x99, 0xa, 0x19, 0x91, 0x14, 0x3f, 0xe6, 0xf0, 0x86, 0xb1, 0xe2, 0xf1, 0xfa, 0x74, 0xf3, 0xb4, 0x6d, 0x21, 0xb2, 0x6a, 0xe3, 0xe7, 0xb5, 0xea, 0x3, 0x8f, 0xd3, 0xc9, 0x42, 0xd4, 0xe8, 0x75, 0x7f, 0xff, 0x7e, 0xfd}
var mulTable = [256][256]uint8{[256]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
var mulTable = [256][256]uint8{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff},
{0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0xd, 0xf, 0x9, 0xb, 0x5, 0x7, 0x1, 0x3, 0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23, 0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43, 0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63, 0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83, 0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3, 0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3, 0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3},
{0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9d, 0x9e, 0x9b, 0x98, 0x91, 0x92, 0x97, 0x94, 0x85, 0x86, 0x83, 0x80, 0x89, 0x8a, 0x8f, 0x8c, 0xad, 0xae, 0xab, 0xa8, 0xa1, 0xa2, 0xa7, 0xa4, 0xb5, 0xb6, 0xb3, 0xb0, 0xb9, 0xba, 0xbf, 0xbc, 0xfd, 0xfe, 0xfb, 0xf8, 0xf1, 0xf2, 0xf7, 0xf4, 0xe5, 0xe6, 0xe3, 0xe0, 0xe9, 0xea, 0xef, 0xec, 0xcd, 0xce, 0xcb, 0xc8, 0xc1, 0xc2, 0xc7, 0xc4, 0xd5, 0xd6, 0xd3, 0xd0, 0xd9, 0xda, 0xdf, 0xdc, 0x5d, 0x5e, 0x5b, 0x58, 0x51, 0x52, 0x57, 0x54, 0x45, 0x46, 0x43, 0x40, 0x49, 0x4a, 0x4f, 0x4c, 0x6d, 0x6e, 0x6b, 0x68, 0x61, 0x62, 0x67, 0x64, 0x75, 0x76, 0x73, 0x70, 0x79, 0x7a, 0x7f, 0x7c, 0x3d, 0x3e, 0x3b, 0x38, 0x31, 0x32, 0x37, 0x34, 0x25, 0x26, 0x23, 0x20, 0x29, 0x2a, 0x2f, 0x2c, 0xd, 0xe, 0xb, 0x8, 0x1, 0x2, 0x7, 0x4, 0x15, 0x16, 0x13, 0x10, 0x19, 0x1a, 0x1f, 0x1c},
@@ -901,7 +901,7 @@ func galExp(a byte, n int) byte {
return expTable[logResult]
}
func genAvx2Matrix(matrixRows [][]byte, inputs, outputs int, dst []byte) []byte {
func genAvx2Matrix(matrixRows [][]byte, inputs, inIdx, outputs int, dst []byte) []byte {
if !avx2CodeGen {
panic("codegen not enabled")
}
@@ -915,14 +915,16 @@ func genAvx2Matrix(matrixRows [][]byte, inputs, outputs int, dst []byte) []byte
dst = dst[:wantBytes]
}
for i, row := range matrixRows[:outputs] {
for j, idx := range row[:inputs] {
for j, idx := range row[inIdx : inIdx+inputs] {
dstIdx := (j*outputs + i) * 64
dstPart := dst[dstIdx:]
dstPart = dstPart[:64]
lo := mulTableLow[idx][:]
hi := mulTableHigh[idx][:]
copy(dst[dstIdx:], lo)
copy(dst[dstIdx+16:], lo)
copy(dst[dstIdx+32:], hi)
copy(dst[dstIdx+48:], hi)
copy(dstPart[:16], lo)
copy(dstPart[16:32], lo)
copy(dstPart[32:48], hi)
copy(dstPart[48:64], hi)
}
}
return dst
+9 -8
View File
@@ -1,6 +1,5 @@
//+build !noasm
//+build !appengine
//+build !gccgo
//go:build !noasm && !appengine && !gccgo
// +build !noasm,!appengine,!gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2019, Minio, Inc.
@@ -105,7 +104,7 @@ func setupMatrix84(matrixRows [][]byte, inputOffset, outputOffset int, matrix *[
// Invoke AVX512 routine for single output row in parallel
func galMulAVX512Parallel81(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix81 *[matrixSize81]byte) {
done := stop - start
if done <= 0 {
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
@@ -140,7 +139,7 @@ func galMulAVX512Parallel81(in, out [][]byte, matrixRows [][]byte, inputOffset,
// Invoke AVX512 routine for 2 output rows in parallel
func galMulAVX512Parallel82(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix82 *[matrixSize82]byte) {
done := stop - start
if done <= 0 {
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
@@ -175,7 +174,7 @@ func galMulAVX512Parallel82(in, out [][]byte, matrixRows [][]byte, inputOffset,
// Invoke AVX512 routine for 4 output rows in parallel
func galMulAVX512Parallel84(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix84 *[matrixSize84]byte) {
done := stop - start
if done <= 0 {
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
@@ -226,8 +225,9 @@ func galMulAVX512LastInput(inputOffset int, inputEnd int, outputOffset int, outp
// Perform the same as codeSomeShards, but taking advantage of
// AVX512 parallelism for up to 4x faster execution as compared to AVX2
func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, byteCount int) {
// Process using no goroutines
outputCount := len(outputs)
start, end := 0, r.o.perRound
if end > byteCount {
end = byteCount
@@ -273,7 +273,8 @@ func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte,
// Perform the same as codeSomeShards, but taking advantage of
// AVX512 parallelism for up to 4x faster execution as compared to AVX2
func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, byteCount int) {
outputCount := len(outputs)
var wg sync.WaitGroup
do := byteCount / r.o.maxGoroutines
if do < r.o.minSplitSize {
+3 -1
View File
@@ -1,4 +1,6 @@
//+build !noasm !appengine !gccgo
//+build !noasm
//+build !appengine
//+build !gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2019, Minio, Inc.
+6 -4
View File
@@ -1,6 +1,5 @@
//+build !noasm
//+build !appengine
//+build !gccgo
//go:build !noasm && !appengine && !gccgo
// +build !noasm,!appengine,!gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
@@ -108,6 +107,9 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
in = in[done:]
out = out[done:]
}
if len(in) == 0 {
return
}
out = out[:len(in)]
mt := mulTable[c][:256]
for i := range in {
@@ -115,7 +117,7 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
}
}
// slice galois add
// simple slice xor
func sliceXor(in, out []byte, o *options) {
if o.useSSE2 {
if len(in) >= bigSwitchover {
+3 -1
View File
@@ -1,4 +1,6 @@
//+build !noasm !appengine !gccgo
//+build !noasm
//+build !appengine
//+build !gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
+3 -4
View File
@@ -1,6 +1,5 @@
//+build !noasm
//+build !appengine
//+build !gccgo
//go:build !noasm && !appengine && !gccgo
// +build !noasm,!appengine,!gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2017, Minio, Inc.
@@ -52,7 +51,7 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
}
}
// slice galois add
// simple slice xor
func sliceXor(in, out []byte, o *options) {
galXorNEON(in, out)
+3 -1
View File
@@ -1,4 +1,6 @@
//+build !noasm !appengine !gccgo
//+build !noasm
//+build !appengine
//+build !gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2017, Minio, Inc.
File diff suppressed because it is too large Load Diff
+54200 -9304
View File
File diff suppressed because it is too large Load Diff
+10 -3
View File
@@ -1,11 +1,18 @@
//+build !amd64 noasm appengine gccgo nogen
//go:build !amd64 || noasm || appengine || gccgo || nogen
// +build !amd64 noasm appengine gccgo nogen
package reedsolomon
const maxAvx2Inputs = 0
const maxAvx2Outputs = 0
const maxAvx2Inputs = 1
const maxAvx2Outputs = 1
const minAvx2Size = 1
const avxSizeMask = 0
const avx2CodeGen = false
func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
panic("avx2 codegen not available")
}
func galMulSlicesAvx2Xor(matrix []byte, in, out [][]byte, start, stop int) int {
panic("avx2 codegen not available")
}
+441 -40
View File
@@ -1,33 +1,36 @@
// Code generated by command: go generate gen.go. DO NOT EDIT.
// +build !appengine
// +build !noasm
// +build gc
// +build !nogen
//go:build !appengine && !noasm && gc && !nogen
// +build !appengine,!noasm,gc,!nogen
package reedsolomon
import "fmt"
import (
"fmt"
)
const avx2CodeGen = true
const maxAvx2Inputs = 10
const maxAvx2Outputs = 8
const (
avx2CodeGen = true
maxAvx2Inputs = 10
maxAvx2Outputs = 10
minAvx2Size = 64
avxSizeMask = maxInt - (minAvx2Size - 1)
)
func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
n := stop - start
n = (n >> 5) << 5
n := (stop - start) & avxSizeMask
switch len(in) {
case 1:
switch len(out) {
case 1:
mulAvxTwo_1x1(matrix, in, out, start, n)
mulAvxTwo_1x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_1x2(matrix, in, out, start, n)
mulAvxTwo_1x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_1x3(matrix, in, out, start, n)
mulAvxTwo_1x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_1x4(matrix, in, out, start, n)
@@ -44,17 +47,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_1x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_1x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_1x10(matrix, in, out, start, n)
return n
}
case 2:
switch len(out) {
case 1:
mulAvxTwo_2x1(matrix, in, out, start, n)
mulAvxTwo_2x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_2x2(matrix, in, out, start, n)
mulAvxTwo_2x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_2x3(matrix, in, out, start, n)
mulAvxTwo_2x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_2x4(matrix, in, out, start, n)
@@ -71,17 +80,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_2x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_2x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_2x10(matrix, in, out, start, n)
return n
}
case 3:
switch len(out) {
case 1:
mulAvxTwo_3x1(matrix, in, out, start, n)
mulAvxTwo_3x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_3x2(matrix, in, out, start, n)
mulAvxTwo_3x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_3x3(matrix, in, out, start, n)
mulAvxTwo_3x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_3x4(matrix, in, out, start, n)
@@ -98,17 +113,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_3x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_3x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_3x10(matrix, in, out, start, n)
return n
}
case 4:
switch len(out) {
case 1:
mulAvxTwo_4x1(matrix, in, out, start, n)
mulAvxTwo_4x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_4x2(matrix, in, out, start, n)
mulAvxTwo_4x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_4x3(matrix, in, out, start, n)
mulAvxTwo_4x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_4x4(matrix, in, out, start, n)
@@ -125,17 +146,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_4x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_4x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_4x10(matrix, in, out, start, n)
return n
}
case 5:
switch len(out) {
case 1:
mulAvxTwo_5x1(matrix, in, out, start, n)
mulAvxTwo_5x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_5x2(matrix, in, out, start, n)
mulAvxTwo_5x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_5x3(matrix, in, out, start, n)
mulAvxTwo_5x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_5x4(matrix, in, out, start, n)
@@ -152,17 +179,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_5x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_5x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_5x10(matrix, in, out, start, n)
return n
}
case 6:
switch len(out) {
case 1:
mulAvxTwo_6x1(matrix, in, out, start, n)
mulAvxTwo_6x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_6x2(matrix, in, out, start, n)
mulAvxTwo_6x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_6x3(matrix, in, out, start, n)
mulAvxTwo_6x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_6x4(matrix, in, out, start, n)
@@ -179,17 +212,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_6x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_6x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_6x10(matrix, in, out, start, n)
return n
}
case 7:
switch len(out) {
case 1:
mulAvxTwo_7x1(matrix, in, out, start, n)
mulAvxTwo_7x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_7x2(matrix, in, out, start, n)
mulAvxTwo_7x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_7x3(matrix, in, out, start, n)
mulAvxTwo_7x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_7x4(matrix, in, out, start, n)
@@ -206,17 +245,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_7x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_7x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_7x10(matrix, in, out, start, n)
return n
}
case 8:
switch len(out) {
case 1:
mulAvxTwo_8x1(matrix, in, out, start, n)
mulAvxTwo_8x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_8x2(matrix, in, out, start, n)
mulAvxTwo_8x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_8x3(matrix, in, out, start, n)
mulAvxTwo_8x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_8x4(matrix, in, out, start, n)
@@ -233,17 +278,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_8x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_8x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_8x10(matrix, in, out, start, n)
return n
}
case 9:
switch len(out) {
case 1:
mulAvxTwo_9x1(matrix, in, out, start, n)
mulAvxTwo_9x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_9x2(matrix, in, out, start, n)
mulAvxTwo_9x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_9x3(matrix, in, out, start, n)
mulAvxTwo_9x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_9x4(matrix, in, out, start, n)
@@ -260,17 +311,23 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_9x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_9x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_9x10(matrix, in, out, start, n)
return n
}
case 10:
switch len(out) {
case 1:
mulAvxTwo_10x1(matrix, in, out, start, n)
mulAvxTwo_10x1_64(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_10x2(matrix, in, out, start, n)
mulAvxTwo_10x2_64(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_10x3(matrix, in, out, start, n)
mulAvxTwo_10x3_64(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_10x4(matrix, in, out, start, n)
@@ -287,6 +344,350 @@ func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
case 8:
mulAvxTwo_10x8(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_10x9(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_10x10(matrix, in, out, start, n)
return n
}
}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
}
func galMulSlicesAvx2Xor(matrix []byte, in, out [][]byte, start, stop int) int {
n := (stop - start) & avxSizeMask
switch len(in) {
case 1:
switch len(out) {
case 1:
mulAvxTwo_1x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_1x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_1x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_1x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_1x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_1x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_1x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_1x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_1x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_1x10Xor(matrix, in, out, start, n)
return n
}
case 2:
switch len(out) {
case 1:
mulAvxTwo_2x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_2x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_2x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_2x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_2x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_2x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_2x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_2x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_2x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_2x10Xor(matrix, in, out, start, n)
return n
}
case 3:
switch len(out) {
case 1:
mulAvxTwo_3x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_3x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_3x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_3x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_3x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_3x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_3x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_3x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_3x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_3x10Xor(matrix, in, out, start, n)
return n
}
case 4:
switch len(out) {
case 1:
mulAvxTwo_4x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_4x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_4x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_4x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_4x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_4x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_4x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_4x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_4x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_4x10Xor(matrix, in, out, start, n)
return n
}
case 5:
switch len(out) {
case 1:
mulAvxTwo_5x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_5x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_5x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_5x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_5x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_5x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_5x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_5x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_5x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_5x10Xor(matrix, in, out, start, n)
return n
}
case 6:
switch len(out) {
case 1:
mulAvxTwo_6x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_6x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_6x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_6x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_6x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_6x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_6x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_6x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_6x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_6x10Xor(matrix, in, out, start, n)
return n
}
case 7:
switch len(out) {
case 1:
mulAvxTwo_7x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_7x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_7x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_7x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_7x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_7x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_7x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_7x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_7x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_7x10Xor(matrix, in, out, start, n)
return n
}
case 8:
switch len(out) {
case 1:
mulAvxTwo_8x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_8x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_8x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_8x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_8x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_8x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_8x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_8x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_8x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_8x10Xor(matrix, in, out, start, n)
return n
}
case 9:
switch len(out) {
case 1:
mulAvxTwo_9x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_9x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_9x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_9x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_9x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_9x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_9x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_9x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_9x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_9x10Xor(matrix, in, out, start, n)
return n
}
case 10:
switch len(out) {
case 1:
mulAvxTwo_10x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulAvxTwo_10x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulAvxTwo_10x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulAvxTwo_10x4Xor(matrix, in, out, start, n)
return n
case 5:
mulAvxTwo_10x5Xor(matrix, in, out, start, n)
return n
case 6:
mulAvxTwo_10x6Xor(matrix, in, out, start, n)
return n
case 7:
mulAvxTwo_10x7Xor(matrix, in, out, start, n)
return n
case 8:
mulAvxTwo_10x8Xor(matrix, in, out, start, n)
return n
case 9:
mulAvxTwo_10x9Xor(matrix, in, out, start, n)
return n
case 10:
mulAvxTwo_10x10Xor(matrix, in, out, start, n)
return n
}
}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
+22 -8
View File
@@ -1,11 +1,14 @@
//+build !amd64 noasm appengine gccgo
//+build !arm64 noasm appengine gccgo
//+build !ppc64le noasm appengine gccgo
//go:build (!amd64 || noasm || appengine || gccgo) && (!arm64 || noasm || appengine || gccgo) && (!ppc64le || noasm || appengine || gccgo)
// +build !amd64 noasm appengine gccgo
// +build !arm64 noasm appengine gccgo
// +build !ppc64le noasm appengine gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
package reedsolomon
import "encoding/binary"
func galMulSlice(c byte, in, out []byte, o *options) {
out = out[:len(in)]
if c == 1 {
@@ -21,9 +24,7 @@ func galMulSlice(c byte, in, out []byte, o *options) {
func galMulSliceXor(c byte, in, out []byte, o *options) {
out = out[:len(in)]
if c == 1 {
for n, input := range in {
out[n] ^= input
}
sliceXor(in, out, o)
return
}
mt := mulTable[c][:256]
@@ -32,8 +33,21 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
}
}
// slice galois add
func sliceXor(in, out []byte, o *options) {
// simple slice xor
func sliceXor(in, out []byte, _ *options) {
for len(out) >= 32 {
inS := in[:32]
v0 := binary.LittleEndian.Uint64(out[:]) ^ binary.LittleEndian.Uint64(inS[:])
v1 := binary.LittleEndian.Uint64(out[8:]) ^ binary.LittleEndian.Uint64(inS[8:])
v2 := binary.LittleEndian.Uint64(out[16:]) ^ binary.LittleEndian.Uint64(inS[16:])
v3 := binary.LittleEndian.Uint64(out[24:]) ^ binary.LittleEndian.Uint64(inS[24:])
binary.LittleEndian.PutUint64(out[:], v0)
binary.LittleEndian.PutUint64(out[8:], v1)
binary.LittleEndian.PutUint64(out[16:], v2)
binary.LittleEndian.PutUint64(out[24:], v3)
out = out[32:]
in = in[32:]
}
for n, input := range in {
out[n] ^= input
}
+4 -3
View File
@@ -1,13 +1,14 @@
//+build !amd64 noasm appengine gccgo
//go:build !amd64 || noasm || appengine || gccgo
// +build !amd64 noasm appengine gccgo
// Copyright 2020, Klaus Post, see LICENSE for details.
package reedsolomon
func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, byteCount int) {
panic("codeSomeShardsAvx512 should not be called if built without asm")
}
func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, byteCount int) {
panic("codeSomeShardsAvx512P should not be called if built without asm")
}
+2 -3
View File
@@ -1,6 +1,5 @@
//+build !noasm
//+build !appengine
//+build !gccgo
//go:build !noasm && !appengine && !gccgo
// +build !noasm,!appengine,!gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2018, Minio, Inc.
+3 -1
View File
@@ -1,4 +1,6 @@
//+build !noasm !appengine !gccgo
//+build !noasm
//+build !appengine
//+build !gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2018, Minio, Inc.
-249
View File
@@ -1,249 +0,0 @@
//+build generate
//go:generate go run gen.go -out galois_gen_amd64.s -stubs galois_gen_amd64.go
//go:generate gofmt -w galois_gen_switch_amd64.go
package main
import (
"bufio"
"fmt"
"os"
. "github.com/mmcloughlin/avo/build"
"github.com/mmcloughlin/avo/buildtags"
. "github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
)
// Technically we can do slightly bigger, but we stay reasonable.
const inputMax = 10
const outputMax = 8
var switchDefs [inputMax][outputMax]string
var switchDefsX [inputMax][outputMax]string
const perLoopBits = 5
const perLoop = 1 << perLoopBits
func main() {
Constraint(buildtags.Not("appengine").ToConstraint())
Constraint(buildtags.Not("noasm").ToConstraint())
Constraint(buildtags.Not("nogen").ToConstraint())
Constraint(buildtags.Term("gc").ToConstraint())
for i := 1; i <= inputMax; i++ {
for j := 1; j <= outputMax; j++ {
//genMulAvx2(fmt.Sprintf("mulAvxTwoXor_%dx%d", i, j), i, j, true)
genMulAvx2(fmt.Sprintf("mulAvxTwo_%dx%d", i, j), i, j, false)
}
}
f, err := os.Create("galois_gen_switch_amd64.go")
if err != nil {
panic(err)
}
defer f.Close()
w := bufio.NewWriter(f)
defer w.Flush()
w.WriteString(`// Code generated by command: go generate ` + os.Getenv("GOFILE") + `. DO NOT EDIT.
// +build !appengine
// +build !noasm
// +build gc
// +build !nogen
package reedsolomon
import "fmt"
`)
w.WriteString("const avx2CodeGen = true\n")
w.WriteString(fmt.Sprintf("const maxAvx2Inputs = %d\nconst maxAvx2Outputs = %d\n", inputMax, outputMax))
w.WriteString(`
func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
n := stop-start
`)
w.WriteString(fmt.Sprintf("n = (n>>%d)<<%d\n\n", perLoopBits, perLoopBits))
w.WriteString(`switch len(in) {
`)
for in, defs := range switchDefs[:] {
w.WriteString(fmt.Sprintf(" case %d:\n switch len(out) {\n", in+1))
for out, def := range defs[:] {
w.WriteString(fmt.Sprintf(" case %d:\n", out+1))
w.WriteString(def)
}
w.WriteString("}\n")
}
w.WriteString(`}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
}
`)
Generate()
}
func genMulAvx2(name string, inputs int, outputs int, xor bool) {
total := inputs * outputs
doc := []string{
fmt.Sprintf("%s takes %d inputs and produces %d outputs.", name, inputs, outputs),
}
if !xor {
doc = append(doc, "The output is initialized to 0.")
}
// Load shuffle masks on every use.
var loadNone bool
// Use registers for destination registers.
var regDst = true
// lo, hi, 1 in, 1 out, 2 tmp, 1 mask
est := total*2 + outputs + 5
if outputs == 1 {
// We don't need to keep a copy of the input if only 1 output.
est -= 2
}
if est > 16 {
loadNone = true
// We run out of GP registers first, now.
if inputs+outputs > 12 {
regDst = false
}
}
TEXT(name, 0, fmt.Sprintf("func(matrix []byte, in [][]byte, out [][]byte, start, n int)"))
// SWITCH DEFINITION:
s := fmt.Sprintf(" mulAvxTwo_%dx%d(matrix, in, out, start, n)\n", inputs, outputs)
s += fmt.Sprintf("\t\t\t\treturn n\n")
switchDefs[inputs-1][outputs-1] = s
if loadNone {
Comment("Loading no tables to registers")
} else {
// loadNone == false
Comment("Loading all tables to registers")
}
Doc(doc...)
Pragma("noescape")
Commentf("Full registers estimated %d YMM used", est)
length := Load(Param("n"), GP64())
matrixBase := GP64()
MOVQ(Param("matrix").Base().MustAddr(), matrixBase)
SHRQ(U8(perLoopBits), length)
TESTQ(length, length)
JZ(LabelRef(name + "_end"))
dst := make([]reg.VecVirtual, outputs)
dstPtr := make([]reg.GPVirtual, outputs)
outBase := Param("out").Base().MustAddr()
outSlicePtr := GP64()
MOVQ(outBase, outSlicePtr)
for i := range dst {
dst[i] = YMM()
if !regDst {
continue
}
ptr := GP64()
MOVQ(Mem{Base: outSlicePtr, Disp: i * 24}, ptr)
dstPtr[i] = ptr
}
inLo := make([]reg.VecVirtual, total)
inHi := make([]reg.VecVirtual, total)
for i := range inLo {
if loadNone {
break
}
tableLo := YMM()
tableHi := YMM()
VMOVDQU(Mem{Base: matrixBase, Disp: i * 64}, tableLo)
VMOVDQU(Mem{Base: matrixBase, Disp: i*64 + 32}, tableHi)
inLo[i] = tableLo
inHi[i] = tableHi
}
inPtrs := make([]reg.GPVirtual, inputs)
inSlicePtr := GP64()
MOVQ(Param("in").Base().MustAddr(), inSlicePtr)
for i := range inPtrs {
ptr := GP64()
MOVQ(Mem{Base: inSlicePtr, Disp: i * 24}, ptr)
inPtrs[i] = ptr
}
tmpMask := GP64()
MOVQ(U32(15), tmpMask)
lowMask := YMM()
MOVQ(tmpMask, lowMask.AsX())
VPBROADCASTB(lowMask.AsX(), lowMask)
offset := GP64()
MOVQ(Param("start").MustAddr(), offset)
Label(name + "_loop")
if xor {
Commentf("Load %d outputs", outputs)
} else {
Commentf("Clear %d outputs", outputs)
}
for i := range dst {
if xor {
if regDst {
VMOVDQU(Mem{Base: dstPtr[i], Index: offset, Scale: 1}, dst[i])
continue
}
ptr := GP64()
MOVQ(outBase, ptr)
VMOVDQU(Mem{Base: ptr, Index: offset, Scale: 1}, dst[i])
} else {
VPXOR(dst[i], dst[i], dst[i])
}
}
lookLow, lookHigh := YMM(), YMM()
inLow, inHigh := YMM(), YMM()
for i := range inPtrs {
Commentf("Load and process 32 bytes from input %d to %d outputs", i, outputs)
VMOVDQU(Mem{Base: inPtrs[i], Index: offset, Scale: 1}, inLow)
VPSRLQ(U8(4), inLow, inHigh)
VPAND(lowMask, inLow, inLow)
VPAND(lowMask, inHigh, inHigh)
for j := range dst {
if loadNone {
VMOVDQU(Mem{Base: matrixBase, Disp: 64 * (i*outputs + j)}, lookLow)
VMOVDQU(Mem{Base: matrixBase, Disp: 32 + 64*(i*outputs+j)}, lookHigh)
VPSHUFB(inLow, lookLow, lookLow)
VPSHUFB(inHigh, lookHigh, lookHigh)
} else {
VPSHUFB(inLow, inLo[i*outputs+j], lookLow)
VPSHUFB(inHigh, inHi[i*outputs+j], lookHigh)
}
VPXOR(lookLow, lookHigh, lookLow)
VPXOR(lookLow, dst[j], dst[j])
}
}
Commentf("Store %d outputs", outputs)
for i := range dst {
if regDst {
VMOVDQU(dst[i], Mem{Base: dstPtr[i], Index: offset, Scale: 1})
continue
}
ptr := GP64()
MOVQ(Mem{Base: outSlicePtr, Disp: i * 24}, ptr)
VMOVDQU(dst[i], Mem{Base: ptr, Index: offset, Scale: 1})
}
Comment("Prepare for next loop")
ADDQ(U8(perLoop), offset)
DECQ(length)
JNZ(LabelRef(name + "_loop"))
VZEROUPPER()
Label(name + "_end")
RET()
}
+2 -4
View File
@@ -1,7 +1,5 @@
module github.com/klauspost/reedsolomon
go 1.14
go 1.15
require (
github.com/klauspost/cpuid v1.2.4
)
require github.com/klauspost/cpuid/v2 v2.0.14
+2 -2
View File
@@ -1,2 +1,2 @@
github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8=
github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
+17 -13
View File
@@ -14,7 +14,7 @@ import (
// The tree uses a Reader-Writer mutex to make it thread-safe
// when accessing cached matrices and inserting new ones.
type inversionTree struct {
mutex *sync.RWMutex
mutex sync.RWMutex
root inversionNode
}
@@ -26,21 +26,22 @@ type inversionNode struct {
// newInversionTree initializes a tree for storing inverted matrices.
// Note that the root node is the identity matrix as it implies
// there were no errors with the original data.
func newInversionTree(dataShards, parityShards int) inversionTree {
func newInversionTree(dataShards, parityShards int) *inversionTree {
identity, _ := identityMatrix(dataShards)
root := inversionNode{
matrix: identity,
children: make([]*inversionNode, dataShards+parityShards),
}
return inversionTree{
mutex: &sync.RWMutex{},
root: root,
return &inversionTree{
root: inversionNode{
matrix: identity,
children: make([]*inversionNode, dataShards+parityShards),
},
}
}
// GetInvertedMatrix returns the cached inverted matrix or nil if it
// is not found in the tree keyed on the indices of invalid rows.
func (t inversionTree) GetInvertedMatrix(invalidIndices []int) matrix {
func (t *inversionTree) GetInvertedMatrix(invalidIndices []int) matrix {
if t == nil {
return nil
}
// Lock the tree for reading before accessing the tree.
t.mutex.RLock()
defer t.mutex.RUnlock()
@@ -63,7 +64,10 @@ var errAlreadySet = errors.New("the root node identity matrix is already set")
// keyed by the indices of invalid rows. The total number of shards
// is required for creating the proper length lists of child nodes for
// each node.
func (t inversionTree) InsertInvertedMatrix(invalidIndices []int, matrix matrix, shards int) error {
func (t *inversionTree) InsertInvertedMatrix(invalidIndices []int, matrix matrix, shards int) error {
if t == nil {
return nil
}
// If no invalid indices were given then we are done because the
// root node is already set with the identity matrix.
if len(invalidIndices) == 0 {
@@ -86,7 +90,7 @@ func (t inversionTree) InsertInvertedMatrix(invalidIndices []int, matrix matrix,
return nil
}
func (n inversionNode) getInvertedMatrix(invalidIndices []int, parent int) matrix {
func (n *inversionNode) getInvertedMatrix(invalidIndices []int, parent int) matrix {
// Get the child node to search next from the list of children. The
// list of children starts relative to the parent index passed in
// because the indices of invalid rows is sorted (by default). As we
@@ -117,7 +121,7 @@ func (n inversionNode) getInvertedMatrix(invalidIndices []int, parent int) matri
return node.matrix
}
func (n inversionNode) insertInvertedMatrix(invalidIndices []int, matrix matrix, shards, parent int) {
func (n *inversionNode) insertInvertedMatrix(invalidIndices []int, matrix matrix, shards, parent int) {
// As above, get the child node to search next from the list of children.
// The list of children starts relative to the parent index passed in
// because the indices of invalid rows is sorted (by default). As we
+4 -1
View File
@@ -218,7 +218,10 @@ func (m matrix) gaussianElimination() error {
if m[r][r] == 0 {
for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
if m[rowBelow][r] != 0 {
m.SwapRows(r, rowBelow)
err := m.SwapRows(r, rowBelow)
if err != nil {
return err
}
break
}
}
+44 -12
View File
@@ -3,7 +3,7 @@ package reedsolomon
import (
"runtime"
"github.com/klauspost/cpuid"
"github.com/klauspost/cpuid/v2"
)
// Option allows to override processing parameters.
@@ -19,6 +19,8 @@ type options struct {
usePAR1Matrix bool
useCauchy bool
fastOneParity bool
inversionCache bool
customMatrix [][]byte
// stream options
concReads bool
@@ -27,15 +29,16 @@ type options struct {
}
var defaultOptions = options{
maxGoroutines: 384,
minSplitSize: -1,
fastOneParity: false,
maxGoroutines: 384,
minSplitSize: -1,
fastOneParity: false,
inversionCache: true,
// Detect CPU capabilities.
useSSSE3: cpuid.CPU.SSSE3(),
useSSE2: cpuid.CPU.SSE2(),
useAVX2: cpuid.CPU.AVX2(),
useAVX512: cpuid.CPU.AVX512F() && cpuid.CPU.AVX512BW(),
useSSSE3: cpuid.CPU.Supports(cpuid.SSSE3),
useSSE2: cpuid.CPU.Supports(cpuid.SSE2),
useAVX2: cpuid.CPU.Supports(cpuid.AVX2),
useAVX512: cpuid.CPU.Supports(cpuid.AVX512F, cpuid.AVX512BW),
}
func init() {
@@ -109,6 +112,15 @@ func WithConcurrentStreamWrites(enabled bool) Option {
}
}
// WithInversionCache allows to control the inversion cache.
// This will cache reconstruction matrices so they can be reused.
// Enabled by default.
func WithInversionCache(enabled bool) Option {
return func(o *options) {
o.inversionCache = enabled
}
}
// WithStreamBlockSize allows to set a custom block size per round of reads/writes.
// If not set, any shard size set with WithAutoGoroutines will be used.
// If WithAutoGoroutines is also unset, 4MB will be used.
@@ -119,25 +131,33 @@ func WithStreamBlockSize(n int) Option {
}
}
func withSSSE3(enabled bool) Option {
// WithSSSE3 allows to enable/disable SSSE3 instructions.
// If not set, SSSE3 will be turned on or off automatically based on CPU ID information.
func WithSSSE3(enabled bool) Option {
return func(o *options) {
o.useSSSE3 = enabled
}
}
func withAVX2(enabled bool) Option {
// WithAVX2 allows to enable/disable AVX2 instructions.
// If not set, AVX2 will be turned on or off automatically based on CPU ID information.
func WithAVX2(enabled bool) Option {
return func(o *options) {
o.useAVX2 = enabled
}
}
func withSSE2(enabled bool) Option {
// WithSSE2 allows to enable/disable SSE2 instructions.
// If not set, SSE2 will be turned on or off automatically based on CPU ID information.
func WithSSE2(enabled bool) Option {
return func(o *options) {
o.useSSE2 = enabled
}
}
func withAVX512(enabled bool) Option {
// WithAVX512 allows to enable/disable AVX512 instructions.
// If not set, AVX512 will be turned on or off automatically based on CPU ID information.
func WithAVX512(enabled bool) Option {
return func(o *options) {
o.useAVX512 = enabled
}
@@ -173,3 +193,15 @@ func WithFastOneParityMatrix() Option {
o.fastOneParity = true
}
}
// WithCustomMatrix causes the encoder to use the manually specified matrix.
// customMatrix represents only the parity chunks.
// customMatrix must have at least ParityShards rows and DataShards columns.
// It can be used for interoperability with libraries which generate
// the matrix differently or to implement more complex coding schemes like LRC
// (locally reconstructible codes).
func WithCustomMatrix(customMatrix [][]byte) Option {
return func(o *options) {
o.customMatrix = customMatrix
}
}
+436 -126
View File
@@ -18,7 +18,7 @@ import (
"runtime"
"sync"
"github.com/klauspost/cpuid"
"github.com/klauspost/cpuid/v2"
)
// Encoder is an interface to encode Reed-Salomon parity sets for your data.
@@ -32,6 +32,12 @@ type Encoder interface {
// data shards while this is running.
Encode(shards [][]byte) error
// EncodeIdx will add parity for a single data shard.
// Parity shards should start out as 0. The caller must zero them.
// Data shards must be delivered exactly once. There is no check for this.
// The parity shards will always be updated and the data shards will remain the same.
EncodeIdx(dataShard []byte, idx int, parity [][]byte) error
// Verify returns true if the parity shards contain correct data.
// The data is the same format as Encode. No data is modified, so
// you are allowed to read from data while this is running.
@@ -71,6 +77,24 @@ type Encoder interface {
// calling the Verify function is likely to fail.
ReconstructData(shards [][]byte) error
// ReconstructSome will recreate only requested data shards, if possible.
//
// Given a list of shards, some of which contain data, fills in the
// data shards indicated by true values in the "required" parameter.
// The length of "required" array must be equal to DataShards.
//
// The length of "shards" array must be equal to Shards.
// You indicate that a shard is missing by setting it to nil or zero-length.
// If a shard is zero-length but has sufficient capacity, that memory will
// be used, otherwise a new []byte will be allocated.
//
// If there are too few shards to reconstruct the missing
// ones, ErrTooFewShards will be returned.
//
// As the reconstructed shard set may contain missing parity shards,
// calling the Verify function is likely to fail.
ReconstructSome(shards [][]byte, required []bool) error
// Update parity is use for change a few data shards and update it's parity.
// Input 'newDatashards' containing data shards changed.
// Input 'shards' containing old data shards (if data shard not changed, it can be nil) and old parity shards.
@@ -102,6 +126,15 @@ type Encoder interface {
Join(dst io.Writer, shards [][]byte, outSize int) error
}
const (
avx2CodeGenMinSize = 64
avx2CodeGenMinShards = 3
avx2CodeGenMaxGoroutines = 8
intSize = 32 << (^uint(0) >> 63) // 32 or 64
maxInt = 1<<(intSize-1) - 1
)
// reedSolomon contains a matrix for a specific
// distribution of datashards and parity shards.
// Construct if using New()
@@ -110,15 +143,16 @@ type reedSolomon struct {
ParityShards int // Number of parity shards, should not be modified.
Shards int // Total number of shards. Calculated, and should not be modified.
m matrix
tree inversionTree
tree *inversionTree
parity [][]byte
o options
mPool sync.Pool
}
// ErrInvShardNum will be returned by New, if you attempt to create
// an Encoder where either data or parity shards is zero or less.
var ErrInvShardNum = errors.New("cannot create Encoder with zero or less data/parity shards")
// an Encoder with less than one data shard or less than zero parity
// shards.
var ErrInvShardNum = errors.New("cannot create Encoder with less than one data shard or less than zero parity shards")
// ErrMaxShardNum will be returned by New, if you attempt to create an
// Encoder where data and parity shards are bigger than the order of
@@ -249,7 +283,7 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
for _, opt := range opts {
opt(&r.o)
}
if dataShards <= 0 || parityShards <= 0 {
if dataShards <= 0 || parityShards < 0 {
return nil, ErrInvShardNum
}
@@ -257,8 +291,28 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
return nil, ErrMaxShardNum
}
if parityShards == 0 {
return &r, nil
}
var err error
switch {
case r.o.customMatrix != nil:
if len(r.o.customMatrix) < parityShards {
return nil, errors.New("coding matrix must contain at least parityShards rows")
}
r.m = make([][]byte, r.Shards)
for i := 0; i < dataShards; i++ {
r.m[i] = make([]byte, dataShards)
r.m[i][i] = 1
}
for k, row := range r.o.customMatrix {
if len(row) < dataShards {
return nil, errors.New("coding matrix must contain at least dataShards columns")
}
r.m[dataShards+k] = make([]byte, dataShards)
copy(r.m[dataShards+k], row)
}
case r.o.fastOneParity && parityShards == 1:
r.m, err = buildXorMatrix(dataShards, r.Shards)
case r.o.useCauchy:
@@ -274,6 +328,24 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
// Calculate what we want per round
r.o.perRound = cpuid.CPU.Cache.L2
divide := parityShards + 1
if avx2CodeGen && r.o.useAVX2 && (dataShards > maxAvx2Inputs || parityShards > maxAvx2Outputs) {
// Base on L1 cache if we have many inputs.
r.o.perRound = cpuid.CPU.Cache.L1D
divide = 0
if dataShards > maxAvx2Inputs {
divide += maxAvx2Inputs
} else {
divide += dataShards
}
if parityShards > maxAvx2Inputs {
divide += maxAvx2Outputs
} else {
divide += parityShards
}
}
if r.o.perRound <= 0 {
// Set to 128K if undetectable.
r.o.perRound = 128 << 10
@@ -283,8 +355,9 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
// If multiple threads per core, make sure they don't contend for cache.
r.o.perRound /= cpuid.CPU.ThreadsPerCore
}
// 1 input + parity must fit in cache, and we add one more to be safer.
r.o.perRound = r.o.perRound / (1 + parityShards)
r.o.perRound = r.o.perRound / divide
// Align to 64 bytes.
r.o.perRound = ((r.o.perRound + 63) / 64) * 64
@@ -302,10 +375,6 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
}
}
if r.o.perRound < r.o.minSplitSize {
r.o.perRound = r.o.minSplitSize
}
if r.o.shardSize > 0 {
p := runtime.GOMAXPROCS(0)
if p == 1 || r.o.shardSize <= r.o.minSplitSize*2 {
@@ -328,12 +397,20 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
}
}
// Generated AVX2 does not need data to stay in L1 cache between runs.
// We will be purely limited by RAM speed.
if r.canAVX2C(avx2CodeGenMinSize, maxAvx2Inputs, maxAvx2Outputs) && r.o.maxGoroutines > avx2CodeGenMaxGoroutines {
r.o.maxGoroutines = avx2CodeGenMaxGoroutines
}
// Inverted matrices are cached in a tree keyed by the indices
// of the invalid rows of the data to reconstruct.
// The inversion root node will have the identity matrix as
// its inversion matrix because it implies there are no errors
// with the original data.
r.tree = newInversionTree(dataShards, parityShards)
if r.o.inversionCache {
r.tree = newInversionTree(dataShards, parityShards)
}
r.parity = make([][]byte, parityShards)
for i := range r.parity {
@@ -341,8 +418,9 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
}
if avx2CodeGen && r.o.useAVX2 {
sz := r.DataShards * r.ParityShards * 2 * 32
r.mPool.New = func() interface{} {
return make([]byte, r.Shards*2*32)
return make([]byte, sz)
}
}
return &r, err
@@ -353,7 +431,7 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
// if there were too few shards to reconstruct the missing data.
var ErrTooFewShards = errors.New("too few shards given")
// Encodes parity for a set of data shards.
// Encode parity for a set of data shards.
// An array 'shards' containing data shards followed by parity shards.
// The number of shards must match the number given to New.
// Each shard is a byte array, and they must all be the same size.
@@ -373,7 +451,49 @@ func (r *reedSolomon) Encode(shards [][]byte) error {
output := shards[r.DataShards:]
// Do the coding.
r.codeSomeShards(r.parity, shards[0:r.DataShards], output, r.ParityShards, len(shards[0]))
r.codeSomeShards(r.parity, shards[0:r.DataShards], output[:r.ParityShards], len(shards[0]))
return nil
}
// EncodeIdx will add parity for a single data shard.
// Parity shards should start out zeroed. The caller must zero them before first call.
// Data shards should only be delivered once. There is no check for this.
// The parity shards will always be updated and the data shards will remain the unchanged.
func (r *reedSolomon) EncodeIdx(dataShard []byte, idx int, parity [][]byte) error {
if len(parity) != r.ParityShards {
return ErrTooFewShards
}
if len(parity) == 0 {
return nil
}
if idx < 0 || idx >= r.DataShards {
return ErrInvShardNum
}
err := checkShards(parity, false)
if err != nil {
return err
}
if len(parity[0]) != len(dataShard) {
return ErrShardSize
}
// Process using no goroutines for now.
start, end := 0, r.o.perRound
if end > len(dataShard) {
end = len(dataShard)
}
for start < len(dataShard) {
in := dataShard[start:end]
for iRow := 0; iRow < r.ParityShards; iRow++ {
galMulSliceXor(r.parity[iRow][idx], in, parity[iRow][start:end], &r.o)
}
start = end
end += r.o.perRound
if end > len(dataShard) {
end = len(dataShard)
}
}
return nil
}
@@ -421,6 +541,10 @@ func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
}
func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
if len(outputs) == 0 {
return
}
if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
r.updateParityShardsP(matrixRows, oldinputs, newinputs, outputs, outputCount, byteCount)
return
@@ -432,7 +556,7 @@ func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outpu
continue
}
oldin := oldinputs[c]
// oldinputs data will be change
// oldinputs data will be changed
sliceXor(in, oldin, &r.o)
for iRow := 0; iRow < outputCount; iRow++ {
galMulSliceXor(matrixRows[iRow][c], oldin, outputs[iRow], &r.o)
@@ -487,7 +611,13 @@ func (r *reedSolomon) Verify(shards [][]byte) (bool, error) {
toCheck := shards[r.DataShards:]
// Do the checking.
return r.checkSomeShards(r.parity, shards[0:r.DataShards], toCheck, r.ParityShards, len(shards[0])), nil
return r.checkSomeShards(r.parity, shards[:r.DataShards], toCheck[:r.ParityShards], len(shards[0])), nil
}
func (r *reedSolomon) canAVX2C(byteCount int, inputs, outputs int) bool {
return avx2CodeGen && r.o.useAVX2 &&
byteCount >= avx2CodeGenMinSize && inputs+outputs >= avx2CodeGenMinShards &&
inputs <= maxAvx2Inputs && outputs <= maxAvx2Outputs
}
// Multiplies a subset of rows from a coding matrix by a full set of
@@ -499,19 +629,19 @@ func (r *reedSolomon) Verify(shards [][]byte) (bool, error) {
// The number of outputs computed, and the
// number of matrix rows used, is determined by
// outputCount, which is the number of outputs to compute.
func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, byteCount int) {
if len(outputs) == 0 {
return
}
switch {
case r.o.useAVX512 && r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize && len(inputs) >= 4 && len(outputs) >= 2:
r.codeSomeShardsAvx512P(matrixRows, inputs, outputs, outputCount, byteCount)
r.codeSomeShardsAvx512P(matrixRows, inputs, outputs, byteCount)
return
case r.o.useAVX512 && len(inputs) >= 4 && len(outputs) >= 2:
r.codeSomeShardsAvx512(matrixRows, inputs, outputs, outputCount, byteCount)
r.codeSomeShardsAvx512(matrixRows, inputs, outputs, byteCount)
return
case r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize:
r.codeSomeShardsP(matrixRows, inputs, outputs, outputCount, byteCount)
case byteCount > r.o.minSplitSize:
r.codeSomeShardsP(matrixRows, inputs, outputs, byteCount)
return
}
@@ -520,17 +650,50 @@ func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, outpu
if end > len(inputs[0]) {
end = len(inputs[0])
}
if avx2CodeGen && r.o.useAVX2 && byteCount >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
m := genAvx2Matrix(matrixRows, len(inputs), len(outputs), r.mPool.Get().([]byte))
if r.canAVX2C(byteCount, len(inputs), len(outputs)) {
m := genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.mPool.Get().([]byte))
start += galMulSlicesAvx2(m, inputs, outputs, 0, byteCount)
r.mPool.Put(m)
end = len(inputs[0])
} else if len(inputs)+len(outputs) > avx2CodeGenMinShards && r.canAVX2C(byteCount, maxAvx2Inputs, maxAvx2Outputs) {
end = len(inputs[0])
inIdx := 0
m := r.mPool.Get().([]byte)
defer r.mPool.Put(m)
ins := inputs
for len(ins) > 0 {
inPer := ins
if len(inPer) > maxAvx2Inputs {
inPer = inPer[:maxAvx2Inputs]
}
outs := outputs
outIdx := 0
for len(outs) > 0 {
outPer := outs
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
m = genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), m)
if inIdx == 0 {
galMulSlicesAvx2(m, inPer, outPer, 0, byteCount)
} else {
galMulSlicesAvx2Xor(m, inPer, outPer, 0, byteCount)
}
start = byteCount & avxSizeMask
outIdx += len(outPer)
outs = outs[len(outPer):]
}
inIdx += len(inPer)
ins = ins[len(inPer):]
}
if start >= end {
return
}
}
for start < len(inputs[0]) {
for c := 0; c < r.DataShards; c++ {
for c := 0; c < len(inputs); c++ {
in := inputs[c][start:end]
for iRow := 0; iRow < outputCount; iRow++ {
for iRow := 0; iRow < len(outputs); iRow++ {
if c == 0 {
galMulSlice(matrixRows[iRow][c], in, outputs[iRow][start:end], &r.o)
} else {
@@ -548,54 +711,227 @@ func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, outpu
// Perform the same as codeSomeShards, but split the workload into
// several goroutines.
func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, byteCount int) {
var wg sync.WaitGroup
do := byteCount / r.o.maxGoroutines
gor := r.o.maxGoroutines
var avx2Matrix []byte
useAvx2 := r.canAVX2C(byteCount, len(inputs), len(outputs))
if useAvx2 {
avx2Matrix = genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.mPool.Get().([]byte))
defer r.mPool.Put(avx2Matrix)
} else if byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
r.canAVX2C(byteCount/4, maxAvx2Inputs, maxAvx2Outputs) {
// It appears there is a switchover point at around 10MB where
// Regular processing is faster...
r.codeSomeShardsAVXP(matrixRows, inputs, outputs, byteCount)
return
}
do := byteCount / gor
if do < r.o.minSplitSize {
do = r.o.minSplitSize
}
exec := func(start, stop int) {
if useAvx2 && stop-start >= 64 {
start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
}
lstart, lstop := start, start+r.o.perRound
if lstop > stop {
lstop = stop
}
for lstart < stop {
for c := 0; c < len(inputs); c++ {
in := inputs[c][lstart:lstop]
for iRow := 0; iRow < len(outputs); iRow++ {
if c == 0 {
galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
} else {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
}
}
}
lstart = lstop
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
}
wg.Done()
}
if gor <= 1 {
wg.Add(1)
exec(0, byteCount)
return
}
// Make sizes divisible by 64
do = (do + 63) & (^63)
start := 0
var avx2Matrix []byte
if avx2CodeGen && r.o.useAVX2 && byteCount >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
avx2Matrix = genAvx2Matrix(matrixRows, len(inputs), len(outputs), r.mPool.Get().([]byte))
defer r.mPool.Put(avx2Matrix)
}
for start < byteCount {
if start+do > byteCount {
do = byteCount - start
}
wg.Add(1)
go func(start, stop int) {
if avx2CodeGen && r.o.useAVX2 && stop-start >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
go exec(start, start+do)
start += do
}
wg.Wait()
}
// Perform the same as codeSomeShards, but split the workload into
// several goroutines.
func (r *reedSolomon) codeSomeShardsAVXP(matrixRows, inputs, outputs [][]byte, byteCount int) {
var wg sync.WaitGroup
gor := r.o.maxGoroutines
type state struct {
input [][]byte
output [][]byte
m []byte
first bool
}
// Make a plan...
plan := make([]state, 0, ((len(inputs)+maxAvx2Inputs-1)/maxAvx2Inputs)*((len(outputs)+maxAvx2Outputs-1)/maxAvx2Outputs))
tmp := r.mPool.Get().([]byte)
defer func(b []byte) {
r.mPool.Put(b)
}(tmp)
// Flips between input first to output first.
// We put the smallest data load in the inner loop.
if len(inputs) > len(outputs) {
inIdx := 0
ins := inputs
for len(ins) > 0 {
inPer := ins
if len(inPer) > maxAvx2Inputs {
inPer = inPer[:maxAvx2Inputs]
}
outs := outputs
outIdx := 0
for len(outs) > 0 {
outPer := outs
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
// Generate local matrix
m := genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), tmp)
tmp = tmp[len(m):]
plan = append(plan, state{
input: inPer,
output: outPer,
m: m,
first: inIdx == 0,
})
outIdx += len(outPer)
outs = outs[len(outPer):]
}
inIdx += len(inPer)
ins = ins[len(inPer):]
}
} else {
outs := outputs
outIdx := 0
for len(outs) > 0 {
outPer := outs
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
lstart, lstop := start, start+r.o.perRound
inIdx := 0
ins := inputs
for len(ins) > 0 {
inPer := ins
if len(inPer) > maxAvx2Inputs {
inPer = inPer[:maxAvx2Inputs]
}
// Generate local matrix
m := genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), tmp)
tmp = tmp[len(m):]
//fmt.Println("bytes:", len(inPer)*r.o.perRound, "out:", len(outPer)*r.o.perRound)
plan = append(plan, state{
input: inPer,
output: outPer,
m: m,
first: inIdx == 0,
})
inIdx += len(inPer)
ins = ins[len(inPer):]
}
outIdx += len(outPer)
outs = outs[len(outPer):]
}
}
do := byteCount / gor
if do < r.o.minSplitSize {
do = r.o.minSplitSize
}
exec := func(start, stop int) {
lstart, lstop := start, start+r.o.perRound
if lstop > stop {
lstop = stop
}
for lstart < stop {
if lstop-lstart >= minAvx2Size {
// Execute plan...
for _, p := range plan {
if p.first {
galMulSlicesAvx2(p.m, p.input, p.output, lstart, lstop)
} else {
galMulSlicesAvx2Xor(p.m, p.input, p.output, lstart, lstop)
}
}
lstart += (lstop - lstart) & avxSizeMask
if lstart == lstop {
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
continue
}
}
for c := range inputs {
in := inputs[c][lstart:lstop]
for iRow := 0; iRow < len(outputs); iRow++ {
if c == 0 {
galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
} else {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
}
}
}
lstart = lstop
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
for lstart < stop {
for c := 0; c < r.DataShards; c++ {
in := inputs[c][lstart:lstop]
for iRow := 0; iRow < outputCount; iRow++ {
if c == 0 {
galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
} else {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
}
}
}
lstart = lstop
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
}
wg.Done()
}(start, start+do)
}
wg.Done()
}
if gor == 1 {
wg.Add(1)
exec(0, byteCount)
return
}
// Make sizes divisible by 64
do = (do + 63) & (^63)
start := 0
for start < byteCount {
if start+do > byteCount {
do = byteCount - start
}
wg.Add(1)
go exec(start, start+do)
start += do
}
wg.Wait()
@@ -604,20 +940,16 @@ func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, outp
// checkSomeShards is mostly the same as codeSomeShards,
// except this will check values and return
// as soon as a difference is found.
func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
return r.checkSomeShardsP(matrixRows, inputs, toCheck, outputCount, byteCount)
func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, byteCount int) bool {
if len(toCheck) == 0 {
return true
}
outputs := make([][]byte, len(toCheck))
for i := range outputs {
outputs[i] = make([]byte, byteCount)
}
for c := 0; c < r.DataShards; c++ {
in := inputs[c]
for iRow := 0; iRow < outputCount; iRow++ {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], &r.o)
}
}
r.codeSomeShards(matrixRows, inputs, outputs, byteCount)
for i, calc := range outputs {
if !bytes.Equal(calc, toCheck[i]) {
@@ -627,57 +959,6 @@ func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, outp
return true
}
func (r *reedSolomon) checkSomeShardsP(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
same := true
var mu sync.RWMutex // For above
var wg sync.WaitGroup
do := byteCount / r.o.maxGoroutines
if do < r.o.minSplitSize {
do = r.o.minSplitSize
}
// Make sizes divisible by 64
do = (do + 63) & (^63)
start := 0
for start < byteCount {
if start+do > byteCount {
do = byteCount - start
}
wg.Add(1)
go func(start, do int) {
defer wg.Done()
outputs := make([][]byte, len(toCheck))
for i := range outputs {
outputs[i] = make([]byte, do)
}
for c := 0; c < r.DataShards; c++ {
mu.RLock()
if !same {
mu.RUnlock()
return
}
mu.RUnlock()
in := inputs[c][start : start+do]
for iRow := 0; iRow < outputCount; iRow++ {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], &r.o)
}
}
for i, calc := range outputs {
if !bytes.Equal(calc, toCheck[i][start:start+do]) {
mu.Lock()
same = false
mu.Unlock()
return
}
}
}(start, do)
start += do
}
wg.Wait()
return same
}
// ErrShardNoData will be returned if there are no shards,
// or if the length of all shards is zero.
var ErrShardNoData = errors.New("no shard data")
@@ -732,7 +1013,7 @@ func shardSize(shards [][]byte) int {
// The reconstructed shard set is complete, but integrity is not verified.
// Use the Verify function to check if data set is ok.
func (r *reedSolomon) Reconstruct(shards [][]byte) error {
return r.reconstruct(shards, false)
return r.reconstruct(shards, false, nil)
}
// ReconstructData will recreate any missing data shards, if possible.
@@ -751,19 +1032,39 @@ func (r *reedSolomon) Reconstruct(shards [][]byte) error {
// As the reconstructed shard set may contain missing parity shards,
// calling the Verify function is likely to fail.
func (r *reedSolomon) ReconstructData(shards [][]byte) error {
return r.reconstruct(shards, true)
return r.reconstruct(shards, true, nil)
}
// ReconstructSome will recreate only requested data shards, if possible.
//
// Given a list of shards, some of which contain data, fills in the
// data shards indicated by true values in the "required" parameter.
// The length of "required" array must be equal to DataShards.
//
// The length of "shards" array must be equal to Shards.
// You indicate that a shard is missing by setting it to nil or zero-length.
// If a shard is zero-length but has sufficient capacity, that memory will
// be used, otherwise a new []byte will be allocated.
//
// If there are too few shards to reconstruct the missing
// ones, ErrTooFewShards will be returned.
//
// As the reconstructed shard set may contain missing parity shards,
// calling the Verify function is likely to fail.
func (r *reedSolomon) ReconstructSome(shards [][]byte, required []bool) error {
return r.reconstruct(shards, true, required)
}
// reconstruct will recreate the missing data shards, and unless
// dataOnly is true, also the missing parity shards
//
// The length of the array must be equal to Shards.
// The length of "shards" array must be equal to Shards.
// You indicate that a shard is missing by setting it to nil.
//
// If there are too few shards to reconstruct the missing
// ones, ErrTooFewShards will be returned.
func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
if len(shards) != r.Shards {
func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []bool) error {
if len(shards) != r.Shards || required != nil && len(required) < r.DataShards {
return ErrTooFewShards
}
// Check arguments.
@@ -778,15 +1079,19 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
// nothing to do.
numberPresent := 0
dataPresent := 0
missingRequired := 0
for i := 0; i < r.Shards; i++ {
if len(shards[i]) != 0 {
numberPresent++
if i < r.DataShards {
dataPresent++
}
} else if required != nil && required[i] {
missingRequired++
}
}
if numberPresent == r.Shards || dataOnly && dataPresent == r.DataShards {
if numberPresent == r.Shards || dataOnly && dataPresent == r.DataShards ||
required != nil && missingRequired == 0 {
// Cool. All of the shards data data. We don't
// need to do anything.
return nil
@@ -864,7 +1169,7 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
outputCount := 0
for iShard := 0; iShard < r.DataShards; iShard++ {
if len(shards[iShard]) == 0 {
if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
if cap(shards[iShard]) >= shardSize {
shards[iShard] = shards[iShard][0:shardSize]
} else {
@@ -875,7 +1180,7 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
outputCount++
}
}
r.codeSomeShards(matrixRows, subShards, outputs[:outputCount], outputCount, shardSize)
r.codeSomeShards(matrixRows, subShards, outputs[:outputCount], shardSize)
if dataOnly {
// Exit out early if we are only interested in the data shards
@@ -890,7 +1195,7 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
// data shards were missing.
outputCount = 0
for iShard := r.DataShards; iShard < r.Shards; iShard++ {
if len(shards[iShard]) == 0 {
if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
if cap(shards[iShard]) >= shardSize {
shards[iShard] = shards[iShard][0:shardSize]
} else {
@@ -901,7 +1206,7 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
outputCount++
}
}
r.codeSomeShards(matrixRows, shards[:r.DataShards], outputs[:outputCount], outputCount, shardSize)
r.codeSomeShards(matrixRows, shards[:r.DataShards], outputs[:outputCount], shardSize)
return nil
}
@@ -925,6 +1230,7 @@ func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
if len(data) == 0 {
return nil, ErrShortData
}
dataLen := len(data)
// Calculate number of bytes per data shard.
perShard := (len(data) + r.DataShards - 1) / r.DataShards
@@ -940,6 +1246,10 @@ func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
padding = make([]byte, r.Shards*perShard-perShard*fullShards)
copy(padding, data[perShard*fullShards:])
data = data[0 : perShard*fullShards]
} else {
for i := dataLen; i < dataLen+r.DataShards; i++ {
data[i] = 0
}
}
// Split into equal-length shards.
-29
View File
@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2018, Michael McLoughlin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-102
View File
@@ -1,102 +0,0 @@
// Package attr provides attributes for text and data sections.
package attr
import (
"fmt"
"math/bits"
"strings"
)
// Attribute represents TEXT or DATA flags.
type Attribute uint16
// Reference: https://github.com/golang/go/blob/aafe257390cc9048e8b5df898fabd79a9e0d4c39/src/runtime/textflag.h#L11-L37
//
// // Don't profile the marked routine. This flag is deprecated.
// #define NOPROF 1
// // It is ok for the linker to get multiple of these symbols. It will
// // pick one of the duplicates to use.
// #define DUPOK 2
// // Don't insert stack check preamble.
// #define NOSPLIT 4
// // Put this data in a read-only section.
// #define RODATA 8
// // This data contains no pointers.
// #define NOPTR 16
// // This is a wrapper function and should not count as disabling 'recover'.
// #define WRAPPER 32
// // This function uses its incoming context register.
// #define NEEDCTXT 64
// // Allocate a word of thread local storage and store the offset from the
// // thread local base to the thread local storage in this variable.
// #define TLSBSS 256
// // Do not insert instructions to allocate a stack frame for this function.
// // Only valid on functions that declare a frame size of 0.
// // TODO(mwhudson): only implemented for ppc64x at present.
// #define NOFRAME 512
// // Function can call reflect.Type.Method or reflect.Type.MethodByName.
// #define REFLECTMETHOD 1024
// // Function is the top of the call stack. Call stack unwinders should stop
// // at this function.
// #define TOPFRAME 2048
//
const (
NOPROF Attribute = 1 << iota
DUPOK
NOSPLIT
RODATA
NOPTR
WRAPPER
NEEDCTXT
_
TLSBSS
NOFRAME
REFLECTMETHOD
TOPFRAME
)
// Asm returns a representation of the attributes in assembly syntax. This may use macros from "textflags.h"; see ContainsTextFlags() to determine if this header is required.
func (a Attribute) Asm() string {
parts, rest := a.split()
if len(parts) == 0 || rest != 0 {
parts = append(parts, fmt.Sprintf("%d", rest))
}
return strings.Join(parts, "|")
}
// ContainsTextFlags returns whether the Asm() representation requires macros in "textflags.h".
func (a Attribute) ContainsTextFlags() bool {
flags, _ := a.split()
return len(flags) > 0
}
// split splits a into known flags and any remaining bits.
func (a Attribute) split() ([]string, Attribute) {
var flags []string
var rest Attribute
for a != 0 {
i := uint(bits.TrailingZeros16(uint16(a)))
bit := Attribute(1) << i
if flag := attrname[bit]; flag != "" {
flags = append(flags, flag)
} else {
rest |= bit
}
a ^= bit
}
return flags, rest
}
var attrname = map[Attribute]string{
NOPROF: "NOPROF",
DUPOK: "DUPOK",
NOSPLIT: "NOSPLIT",
RODATA: "RODATA",
NOPTR: "NOPTR",
WRAPPER: "WRAPPER",
NEEDCTXT: "NEEDCTXT",
TLSBSS: "TLSBSS",
NOFRAME: "NOFRAME",
REFLECTMETHOD: "REFLECTMETHOD",
TOPFRAME: "TOPFRAME",
}
-18
View File
@@ -1,18 +0,0 @@
package build
import "github.com/mmcloughlin/avo/attr"
// TEXT and DATA attribute values included for convenience.
const (
NOPROF = attr.NOPROF
DUPOK = attr.DUPOK
NOSPLIT = attr.NOSPLIT
RODATA = attr.RODATA
NOPTR = attr.NOPTR
WRAPPER = attr.WRAPPER
NEEDCTXT = attr.NEEDCTXT
TLSBSS = attr.TLSBSS
NOFRAME = attr.NOFRAME
REFLECTMETHOD = attr.REFLECTMETHOD
TOPFRAME = attr.TOPFRAME
)
-171
View File
@@ -1,171 +0,0 @@
package build
import (
"flag"
"io"
"log"
"os"
"runtime/pprof"
"github.com/mmcloughlin/avo/pass"
"github.com/mmcloughlin/avo/printer"
)
// Config contains options for an avo main function.
type Config struct {
ErrOut io.Writer
MaxErrors int // max errors to report; 0 means unlimited
CPUProfile io.WriteCloser
Passes []pass.Interface
}
// Main is the standard main function for an avo program. This extracts the
// result from the build Context (logging and exiting on error), and performs
// configured passes.
func Main(cfg *Config, context *Context) int {
diag := log.New(cfg.ErrOut, "", 0)
if cfg.CPUProfile != nil {
defer cfg.CPUProfile.Close()
if err := pprof.StartCPUProfile(cfg.CPUProfile); err != nil {
diag.Println("could not start CPU profile: ", err)
return 1
}
defer pprof.StopCPUProfile()
}
f, err := context.Result()
if err != nil {
LogError(diag, err, cfg.MaxErrors)
return 1
}
p := pass.Concat(cfg.Passes...)
if err := p.Execute(f); err != nil {
diag.Println(err)
return 1
}
return 0
}
// Flags represents CLI flags for an avo program.
type Flags struct {
errout *outputValue
allerrors bool
cpuprof *outputValue
pkg string
printers []*printerValue
}
// NewFlags initializes avo flags for the given FlagSet.
func NewFlags(fs *flag.FlagSet) *Flags {
f := &Flags{}
f.errout = newOutputValue(os.Stderr)
fs.Var(f.errout, "log", "diagnostics output")
fs.BoolVar(&f.allerrors, "e", false, "no limit on number of errors reported")
f.cpuprof = newOutputValue(nil)
fs.Var(f.cpuprof, "cpuprofile", "write cpu profile to `file`")
fs.StringVar(&f.pkg, "pkg", "", "package name (defaults to current directory name)")
goasm := newPrinterValue(printer.NewGoAsm, os.Stdout)
fs.Var(goasm, "out", "assembly output")
f.printers = append(f.printers, goasm)
stubs := newPrinterValue(printer.NewStubs, nil)
fs.Var(stubs, "stubs", "go stub file")
f.printers = append(f.printers, stubs)
return f
}
// Config builds a configuration object based on flag values.
func (f *Flags) Config() *Config {
pc := printer.NewGoRunConfig()
if f.pkg != "" {
pc.Pkg = f.pkg
}
passes := []pass.Interface{pass.Compile}
for _, pv := range f.printers {
p := pv.Build(pc)
if p != nil {
passes = append(passes, p)
}
}
cfg := &Config{
ErrOut: f.errout.w,
MaxErrors: 10,
CPUProfile: f.cpuprof.w,
Passes: passes,
}
if f.allerrors {
cfg.MaxErrors = 0
}
return cfg
}
type outputValue struct {
w io.WriteCloser
filename string
}
func newOutputValue(dflt io.WriteCloser) *outputValue {
return &outputValue{w: dflt}
}
func (o *outputValue) String() string {
if o == nil {
return ""
}
return o.filename
}
func (o *outputValue) Set(s string) error {
o.filename = s
if s == "-" {
o.w = nopwritecloser{os.Stdout}
return nil
}
f, err := os.Create(s)
if err != nil {
return err
}
o.w = f
return nil
}
type printerValue struct {
*outputValue
Builder printer.Builder
}
func newPrinterValue(b printer.Builder, dflt io.WriteCloser) *printerValue {
return &printerValue{
outputValue: newOutputValue(dflt),
Builder: b,
}
}
func (p *printerValue) Build(cfg printer.Config) pass.Interface {
if p.outputValue.w == nil {
return nil
}
return &pass.Output{
Writer: p.outputValue.w,
Printer: p.Builder(cfg),
}
}
// nopwritecloser wraps a Writer and provides a null implementation of Close().
type nopwritecloser struct {
io.Writer
}
func (nopwritecloser) Close() error { return nil }
-223
View File
@@ -1,223 +0,0 @@
package build
import (
"errors"
"fmt"
"go/types"
"golang.org/x/tools/go/packages"
"github.com/mmcloughlin/avo/attr"
"github.com/mmcloughlin/avo/buildtags"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
)
// Context maintains state for incrementally building an avo File.
type Context struct {
pkg *packages.Package
file *ir.File
function *ir.Function
global *ir.Global
errs ErrorList
reg.Collection
}
// NewContext initializes an empty build Context.
func NewContext() *Context {
return &Context{
file: ir.NewFile(),
Collection: *reg.NewCollection(),
}
}
// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
func (c *Context) Package(path string) {
cfg := &packages.Config{
Mode: packages.NeedTypes | packages.NeedDeps | packages.NeedImports,
}
pkgs, err := packages.Load(cfg, path)
if err != nil {
c.adderror(err)
return
}
pkg := pkgs[0]
if len(pkg.Errors) > 0 {
for _, err := range pkg.Errors {
c.adderror(err)
}
return
}
c.pkg = pkg
}
// Constraints sets build constraints for the file.
func (c *Context) Constraints(t buildtags.ConstraintsConvertable) {
cs := t.ToConstraints()
if err := cs.Validate(); err != nil {
c.adderror(err)
return
}
c.file.Constraints = cs
}
// Constraint appends a constraint to the file's build constraints.
func (c *Context) Constraint(t buildtags.ConstraintConvertable) {
c.Constraints(append(c.file.Constraints, t.ToConstraint()))
}
// ConstraintExpr appends a constraint to the file's build constraints. The
// constraint to add is parsed from the given expression. The expression should
// look the same as the content following "// +build " in regular build
// constraint comments.
func (c *Context) ConstraintExpr(expr string) {
constraint, err := buildtags.ParseConstraint(expr)
if err != nil {
c.adderror(err)
return
}
c.Constraint(constraint)
}
// Function starts building a new function with the given name.
func (c *Context) Function(name string) {
c.function = ir.NewFunction(name)
c.file.AddSection(c.function)
}
// Doc sets documentation comment lines for the currently active function.
func (c *Context) Doc(lines ...string) {
c.activefunc().Doc = lines
}
// Pragma adds a compiler directive to the currently active function.
func (c *Context) Pragma(directive string, args ...string) {
c.activefunc().AddPragma(directive, args...)
}
// Attributes sets function attributes for the currently active function.
func (c *Context) Attributes(a attr.Attribute) {
c.activefunc().Attributes = a
}
// Signature sets the signature for the currently active function.
func (c *Context) Signature(s *gotypes.Signature) {
c.activefunc().SetSignature(s)
}
// SignatureExpr parses the signature expression and sets it as the active function's signature.
func (c *Context) SignatureExpr(expr string) {
s, err := gotypes.ParseSignatureInPackage(c.types(), expr)
if err != nil {
c.adderror(err)
return
}
c.Signature(s)
}
// Implement starts building a function of the given name, whose type is
// specified by a stub in the containing package.
func (c *Context) Implement(name string) {
pkg := c.types()
if pkg == nil {
c.adderrormessage("no package specified")
return
}
s, err := gotypes.LookupSignature(pkg, name)
if err != nil {
c.adderror(err)
return
}
c.Function(name)
c.Signature(s)
}
func (c *Context) types() *types.Package {
if c.pkg == nil {
return nil
}
return c.pkg.Types
}
// AllocLocal allocates size bytes in the stack of the currently active function.
// Returns a reference to the base pointer for the newly allocated region.
func (c *Context) AllocLocal(size int) operand.Mem {
return c.activefunc().AllocLocal(size)
}
// Instruction adds an instruction to the active function.
func (c *Context) Instruction(i *ir.Instruction) {
c.activefunc().AddInstruction(i)
}
// Label adds a label to the active function.
func (c *Context) Label(name string) {
c.activefunc().AddLabel(ir.Label(name))
}
// Comment adds comment lines to the active function.
func (c *Context) Comment(lines ...string) {
c.activefunc().AddComment(lines...)
}
// Commentf adds a formtted comment line.
func (c *Context) Commentf(format string, a ...interface{}) {
c.Comment(fmt.Sprintf(format, a...))
}
func (c *Context) activefunc() *ir.Function {
if c.function == nil {
c.adderrormessage("no active function")
return ir.NewFunction("")
}
return c.function
}
//go:generate avogen -output zinstructions.go build
// StaticGlobal adds a new static data section to the file and returns a pointer to it.
func (c *Context) StaticGlobal(name string) operand.Mem {
c.global = ir.NewStaticGlobal(name)
c.file.AddSection(c.global)
return c.global.Base()
}
// DataAttributes sets the attributes on the current active global data section.
func (c *Context) DataAttributes(a attr.Attribute) {
c.activeglobal().Attributes = a
}
// AddDatum adds constant v at offset to the current active global data section.
func (c *Context) AddDatum(offset int, v operand.Constant) {
if err := c.activeglobal().AddDatum(ir.NewDatum(offset, v)); err != nil {
c.adderror(err)
}
}
// AppendDatum appends a constant to the current active global data section.
func (c *Context) AppendDatum(v operand.Constant) {
c.activeglobal().Append(v)
}
func (c *Context) activeglobal() *ir.Global {
if c.global == nil {
c.adderrormessage("no active global")
return ir.NewStaticGlobal("")
}
return c.global
}
func (c *Context) adderror(err error) {
c.errs.addext(err)
}
func (c *Context) adderrormessage(msg string) {
c.adderror(errors.New(msg))
}
// Result returns the built file and any accumulated errors.
func (c *Context) Result() (*ir.File, error) {
return c.file, c.errs.Err()
}
-2
View File
@@ -1,2 +0,0 @@
// Package build provides an assembly-like interface for incremental building of avo Files.
package build
-88
View File
@@ -1,88 +0,0 @@
package build
import (
"fmt"
"log"
"github.com/mmcloughlin/avo/internal/stack"
"github.com/mmcloughlin/avo/src"
)
// Error represents an error during building, optionally tagged with the position at which it happened.
type Error struct {
Position src.Position
Err error
}
// exterr constructs an Error with position derived from the first frame in the
// call stack outside this package.
func exterr(err error) Error {
e := Error{Err: err}
if f := stack.ExternalCaller(); f != nil {
e.Position = src.FramePosition(*f).Relwd()
}
return e
}
func (e Error) Error() string {
msg := e.Err.Error()
if e.Position.IsValid() {
return e.Position.String() + ": " + msg
}
return msg
}
// ErrorList is a collection of errors for a source file.
type ErrorList []Error
// Add appends an error to the list.
func (e *ErrorList) Add(err Error) {
*e = append(*e, err)
}
// AddAt appends an error at position p.
func (e *ErrorList) AddAt(p src.Position, err error) {
e.Add(Error{p, err})
}
// addext appends an error to the list, tagged with the
func (e *ErrorList) addext(err error) {
e.Add(exterr(err))
}
// Err returns an error equivalent to this error list.
// If the list is empty, Err returns nil.
func (e ErrorList) Err() error {
if len(e) == 0 {
return nil
}
return e
}
// An ErrorList implements the error interface.
func (e ErrorList) Error() string {
switch len(e) {
case 0:
return "no errors"
case 1:
return e[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", e[0], len(e)-1)
}
// LogError logs a list of errors, one error per line, if the err parameter is
// an ErrorList. Otherwise it just logs the err string. Reports at most max
// errors, or unlimited if max is 0.
func LogError(l *log.Logger, err error, max int) {
if list, ok := err.(ErrorList); ok {
for i, e := range list {
if max > 0 && i == max {
l.Print("too many errors")
return
}
l.Printf("%s\n", e)
}
} else if err != nil {
l.Printf("%s\n", err)
}
}
-155
View File
@@ -1,155 +0,0 @@
package build
import (
"flag"
"os"
"github.com/mmcloughlin/avo/attr"
"github.com/mmcloughlin/avo/buildtags"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
)
// ctx provides a global build context.
var ctx = NewContext()
// TEXT starts building a new function called name, with attributes a, and sets its signature (see SignatureExpr).
func TEXT(name string, a attr.Attribute, signature string) {
ctx.Function(name)
ctx.Attributes(a)
ctx.SignatureExpr(signature)
}
// GLOBL declares a new static global data section with the given attributes.
func GLOBL(name string, a attr.Attribute) operand.Mem {
// TODO(mbm): should this be static?
g := ctx.StaticGlobal(name)
ctx.DataAttributes(a)
return g
}
// DATA adds a data value to the active data section.
func DATA(offset int, v operand.Constant) {
ctx.AddDatum(offset, v)
}
var flags = NewFlags(flag.CommandLine)
// Generate builds and compiles the avo file built with the global context. This
// should be the final line of any avo program. Configuration is determined from command-line flags.
func Generate() {
if !flag.Parsed() {
flag.Parse()
}
cfg := flags.Config()
status := Main(cfg, ctx)
// To record coverage of integration tests we wrap main() functions in a test
// functions. In this case we need the main function to terminate, therefore we
// only exit for failure status codes.
if status != 0 {
os.Exit(status)
}
}
// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
func Package(path string) { ctx.Package(path) }
// Constraints sets build constraints for the file.
func Constraints(t buildtags.ConstraintsConvertable) { ctx.Constraints(t) }
// Constraint appends a constraint to the file's build constraints.
func Constraint(t buildtags.ConstraintConvertable) { ctx.Constraint(t) }
// ConstraintExpr appends a constraint to the file's build constraints. The
// constraint to add is parsed from the given expression. The expression should
// look the same as the content following "// +build " in regular build
// constraint comments.
func ConstraintExpr(expr string) { ctx.ConstraintExpr(expr) }
// GP8L allocates and returns a general-purpose 8-bit register (low byte).
func GP8L() reg.GPVirtual { return ctx.GP8L() }
// GP8H allocates and returns a general-purpose 8-bit register (high byte).
func GP8H() reg.GPVirtual { return ctx.GP8H() }
// GP8 allocates and returns a general-purpose 8-bit register (low byte).
func GP8() reg.GPVirtual { return ctx.GP8() }
// GP16 allocates and returns a general-purpose 16-bit register.
func GP16() reg.GPVirtual { return ctx.GP16() }
// GP32 allocates and returns a general-purpose 32-bit register.
func GP32() reg.GPVirtual { return ctx.GP32() }
// GP64 allocates and returns a general-purpose 64-bit register.
func GP64() reg.GPVirtual { return ctx.GP64() }
// XMM allocates and returns a 128-bit vector register.
func XMM() reg.VecVirtual { return ctx.XMM() }
// YMM allocates and returns a 256-bit vector register.
func YMM() reg.VecVirtual { return ctx.YMM() }
// ZMM allocates and returns a 512-bit vector register.
func ZMM() reg.VecVirtual { return ctx.ZMM() }
// Param returns a the named argument of the active function.
func Param(name string) gotypes.Component { return ctx.Param(name) }
// ParamIndex returns the ith argument of the active function.
func ParamIndex(i int) gotypes.Component { return ctx.ParamIndex(i) }
// Return returns a the named return value of the active function.
func Return(name string) gotypes.Component { return ctx.Return(name) }
// ReturnIndex returns the ith argument of the active function.
func ReturnIndex(i int) gotypes.Component { return ctx.ReturnIndex(i) }
// Load the function argument src into register dst. Returns the destination
// register. This is syntactic sugar: it will attempt to select the right MOV
// instruction based on the types involved.
func Load(src gotypes.Component, dst reg.Register) reg.Register { return ctx.Load(src, dst) }
// Store register src into return value dst. This is syntactic sugar: it will
// attempt to select the right MOV instruction based on the types involved.
func Store(src reg.Register, dst gotypes.Component) { ctx.Store(src, dst) }
// Dereference loads a pointer and returns its element type.
func Dereference(ptr gotypes.Component) gotypes.Component { return ctx.Dereference(ptr) }
// Doc sets documentation comment lines for the currently active function.
func Doc(lines ...string) { ctx.Doc(lines...) }
// Pragma adds a compiler directive to the currently active function.
func Pragma(directive string, args ...string) { ctx.Pragma(directive, args...) }
// Attributes sets function attributes for the currently active function.
func Attributes(a attr.Attribute) { ctx.Attributes(a) }
// Implement starts building a function of the given name, whose type is
// specified by a stub in the containing package.
func Implement(name string) { ctx.Implement(name) }
// AllocLocal allocates size bytes in the stack of the currently active function.
// Returns a reference to the base pointer for the newly allocated region.
func AllocLocal(size int) operand.Mem { return ctx.AllocLocal(size) }
// Label adds a label to the active function.
func Label(name string) { ctx.Label(name) }
// Comment adds comment lines to the active function.
func Comment(lines ...string) { ctx.Comment(lines...) }
// Commentf adds a formtted comment line.
func Commentf(format string, a ...interface{}) { ctx.Commentf(format, a...) }
// ConstData builds a static data section containing just the given constant.
func ConstData(name string, v operand.Constant) operand.Mem { return ctx.ConstData(name, v) }
// Instruction adds an instruction to the active function.
func Instruction(i *ir.Instruction) { ctx.Instruction(i) }
-70
View File
@@ -1,70 +0,0 @@
package build
import (
"github.com/mmcloughlin/avo/attr"
"github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
"github.com/mmcloughlin/avo/gotypes"
)
//go:generate avogen -output zmov.go mov
// Param returns a the named argument of the active function.
func (c *Context) Param(name string) gotypes.Component {
return c.activefunc().Signature.Params().Lookup(name)
}
// ParamIndex returns the ith argument of the active function.
func (c *Context) ParamIndex(i int) gotypes.Component {
return c.activefunc().Signature.Params().At(i)
}
// Return returns a the named return value of the active function.
func (c *Context) Return(name string) gotypes.Component {
return c.activefunc().Signature.Results().Lookup(name)
}
// ReturnIndex returns the ith argument of the active function.
func (c *Context) ReturnIndex(i int) gotypes.Component {
return c.activefunc().Signature.Results().At(i)
}
// Load the function argument src into register dst. Returns the destination
// register. This is syntactic sugar: it will attempt to select the right MOV
// instruction based on the types involved.
func (c *Context) Load(src gotypes.Component, dst reg.Register) reg.Register {
b, err := src.Resolve()
if err != nil {
c.adderror(err)
return dst
}
c.mov(b.Addr, dst, int(gotypes.Sizes.Sizeof(b.Type)), int(dst.Size()), b.Type)
return dst
}
// Store register src into return value dst. This is syntactic sugar: it will
// attempt to select the right MOV instruction based on the types involved.
func (c *Context) Store(src reg.Register, dst gotypes.Component) {
b, err := dst.Resolve()
if err != nil {
c.adderror(err)
return
}
c.mov(src, b.Addr, int(src.Size()), int(gotypes.Sizes.Sizeof(b.Type)), b.Type)
}
// Dereference loads a pointer and returns its element type.
func (c *Context) Dereference(ptr gotypes.Component) gotypes.Component {
r := c.GP64()
c.Load(ptr, r)
return ptr.Dereference(r)
}
// ConstData builds a static data section containing just the given constant.
func (c *Context) ConstData(name string, v operand.Constant) operand.Mem {
g := c.StaticGlobal(name)
c.DataAttributes(attr.RODATA | attr.NOPTR)
c.AppendDatum(v)
return g
}
File diff suppressed because it is too large Load Diff
-72
View File
@@ -1,72 +0,0 @@
// Code generated by command: avogen -output zmov.go mov. DO NOT EDIT.
package build
import (
"go/types"
"github.com/mmcloughlin/avo/operand"
)
func (c *Context) mov(a, b operand.Op, an, bn int, t *types.Basic) {
switch {
case (t.Info()&types.IsInteger) != 0 && an == 1 && bn == 1:
c.MOVB(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 1 && bn == 4:
c.MOVBLSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 1 && bn == 4:
c.MOVBLZX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 1 && bn == 8:
c.MOVBQSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 1 && bn == 8:
c.MOVBQZX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 1 && bn == 2:
c.MOVBWSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 1 && bn == 2:
c.MOVBWZX(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 4 && bn == 4:
c.MOVL(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 4 && bn == 8:
c.MOVLQSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 4 && bn == 8:
c.MOVLQZX(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 16 && bn == 16:
c.MOVOU(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 4 && bn == 16:
c.MOVQ(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 8 && bn == 8:
c.MOVQ(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 8 && bn == 16:
c.MOVQ(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 16 && bn == 4:
c.MOVQ(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 16 && bn == 8:
c.MOVQ(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 16 && bn == 16:
c.MOVQ(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 8 && bn == 16:
c.MOVSD(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 16 && bn == 8:
c.MOVSD(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 16 && bn == 16:
c.MOVSD(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 4 && bn == 16:
c.MOVSS(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 16 && bn == 4:
c.MOVSS(a, b)
case (t.Info()&types.IsFloat) != 0 && an == 16 && bn == 16:
c.MOVSS(a, b)
case (t.Info()&types.IsInteger) != 0 && an == 2 && bn == 2:
c.MOVW(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 2 && bn == 4:
c.MOVWLSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 2 && bn == 4:
c.MOVWLZX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) == 0 && an == 2 && bn == 8:
c.MOVWQSX(a, b)
case (t.Info()&types.IsInteger) != 0 && (t.Info()&types.IsUnsigned) != 0 && an == 2 && bn == 8:
c.MOVWQZX(a, b)
default:
c.adderrormessage("could not deduce mov instruction")
}
}
-312
View File
@@ -1,312 +0,0 @@
// Package buildtags provides types for representing and manipulating build constraints.
//
// In Go, build constraints are represented as comments in source code together with file naming conventions. For example
//
// // +build linux,386 darwin,!cgo
// // +build !purego
//
// Any terms provided in the filename can be thought of as an implicit extra
// constraint comment line. Collectively, these are referred to as
// ``constraints''. Each line is a ``constraint''. Within each constraint the
// space-separated terms are ``options'', and within that the comma-separated
// items are ``terms'' which may be negated with at most one exclaimation mark.
//
// These represent a boolean formulae. The constraints are evaluated as the AND
// of constraint lines; a constraint is evaluated as the OR of its options and
// an option is evaluated as the AND of its terms. Overall build constraints are
// a boolean formula that is an AND of ORs of ANDs.
//
// This level of complexity is rarely used in Go programs. Therefore this
// package aims to provide access to all these layers of nesting if required,
// but make it easy to forget about for basic use cases too.
package buildtags
import (
"errors"
"fmt"
"strings"
"unicode"
)
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/go/build/doc.go#L73-L92
//
// // A build constraint is evaluated as the OR of space-separated options;
// // each option evaluates as the AND of its comma-separated terms;
// // and each term is an alphanumeric word or, preceded by !, its negation.
// // That is, the build constraint:
// //
// // // +build linux,386 darwin,!cgo
// //
// // corresponds to the boolean formula:
// //
// // (linux AND 386) OR (darwin AND (NOT cgo))
// //
// // A file may have multiple build constraints. The overall constraint is the AND
// // of the individual constraints. That is, the build constraints:
// //
// // // +build linux darwin
// // // +build 386
// //
// // corresponds to the boolean formula:
// //
// // (linux OR darwin) AND 386
//
// Interface represents a build constraint.
type Interface interface {
ConstraintsConvertable
fmt.GoStringer
Evaluate(v map[string]bool) bool
Validate() error
}
// ConstraintsConvertable can be converted to a Constraints object.
type ConstraintsConvertable interface {
ToConstraints() Constraints
}
// ConstraintConvertable can be converted to a Constraint.
type ConstraintConvertable interface {
ToConstraint() Constraint
}
// OptionConvertable can be converted to an Option.
type OptionConvertable interface {
ToOption() Option
}
// Constraints represents the AND of a list of Constraint lines.
type Constraints []Constraint
// And builds Constraints that will be true if all of its constraints are true.
func And(cs ...ConstraintConvertable) Constraints {
constraints := Constraints{}
for _, c := range cs {
constraints = append(constraints, c.ToConstraint())
}
return constraints
}
// ToConstraints returns cs.
func (cs Constraints) ToConstraints() Constraints { return cs }
// Validate validates the constraints set.
func (cs Constraints) Validate() error {
for _, c := range cs {
if err := c.Validate(); err != nil {
return err
}
}
return nil
}
// Evaluate the boolean formula represented by cs under the given assignment of
// tag values. This is the AND of the values of the constituent Constraints.
func (cs Constraints) Evaluate(v map[string]bool) bool {
r := true
for _, c := range cs {
r = r && c.Evaluate(v)
}
return r
}
// GoString represents Constraints as +build comment lines.
func (cs Constraints) GoString() string {
s := ""
for _, c := range cs {
s += c.GoString()
}
return s
}
// Constraint represents the OR of a list of Options.
type Constraint []Option
// Any builds a Constraint that will be true if any of its options are true.
func Any(opts ...OptionConvertable) Constraint {
c := Constraint{}
for _, opt := range opts {
c = append(c, opt.ToOption())
}
return c
}
// ParseConstraint parses a space-separated list of options.
func ParseConstraint(expr string) (Constraint, error) {
c := Constraint{}
for _, field := range strings.Fields(expr) {
opt, err := ParseOption(field)
if err != nil {
return c, err
}
c = append(c, opt)
}
return c, nil
}
// ToConstraints returns the list of constraints containing just c.
func (c Constraint) ToConstraints() Constraints { return Constraints{c} }
// ToConstraint returns c.
func (c Constraint) ToConstraint() Constraint { return c }
// Validate validates the constraint.
func (c Constraint) Validate() error {
for _, o := range c {
if err := o.Validate(); err != nil {
return err
}
}
return nil
}
// Evaluate the boolean formula represented by c under the given assignment of
// tag values. This is the OR of the values of the constituent Options.
func (c Constraint) Evaluate(v map[string]bool) bool {
r := false
for _, o := range c {
r = r || o.Evaluate(v)
}
return r
}
// GoString represents the Constraint as one +build comment line.
func (c Constraint) GoString() string {
s := "// +build"
for _, o := range c {
s += " " + o.GoString()
}
return s + "\n"
}
// Option represents the AND of a list of Terms.
type Option []Term
// Opt builds an Option from the list of Terms.
func Opt(terms ...Term) Option {
return Option(terms)
}
// ParseOption parses a comma-separated list of terms.
func ParseOption(expr string) (Option, error) {
opt := Option{}
for _, t := range strings.Split(expr, ",") {
opt = append(opt, Term(t))
}
return opt, opt.Validate()
}
// ToConstraints returns Constraints containing just this option.
func (o Option) ToConstraints() Constraints { return o.ToConstraint().ToConstraints() }
// ToConstraint returns a Constraint containing just this option.
func (o Option) ToConstraint() Constraint { return Constraint{o} }
// ToOption returns o.
func (o Option) ToOption() Option { return o }
// Validate validates o.
func (o Option) Validate() error {
for _, t := range o {
if err := t.Validate(); err != nil {
return fmt.Errorf("invalid term \"%s\": %s", t, err)
}
}
return nil
}
// Evaluate the boolean formula represented by o under the given assignment of
// tag values. This is the AND of the values of the constituent Terms.
func (o Option) Evaluate(v map[string]bool) bool {
r := true
for _, t := range o {
r = r && t.Evaluate(v)
}
return r
}
// GoString represents the Option as a comma-separated list of terms.
func (o Option) GoString() string {
var ts []string
for _, t := range o {
ts = append(ts, t.GoString())
}
return strings.Join(ts, ",")
}
// Term is an atomic term in a build constraint: an identifier or its negation.
type Term string
// Not returns a term for the negation of ident.
func Not(ident string) Term {
return Term("!" + ident)
}
// ToConstraints returns Constraints containing just this term.
func (t Term) ToConstraints() Constraints { return t.ToOption().ToConstraints() }
// ToConstraint returns a Constraint containing just this term.
func (t Term) ToConstraint() Constraint { return t.ToOption().ToConstraint() }
// ToOption returns an Option containing just this term.
func (t Term) ToOption() Option { return Option{t} }
// IsNegated reports whether t is the negation of an identifier.
func (t Term) IsNegated() bool { return strings.HasPrefix(string(t), "!") }
// Name returns the identifier for this term.
func (t Term) Name() string {
return strings.TrimPrefix(string(t), "!")
}
// Validate the term.
func (t Term) Validate() error {
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L110-L112
//
// if strings.HasPrefix(name, "!!") { // bad syntax, reject always
// return false
// }
//
if strings.HasPrefix(string(t), "!!") {
return errors.New("at most one '!' allowed")
}
if len(t.Name()) == 0 {
return errors.New("empty tag name")
}
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L121-L127
//
// // Tags must be letters, digits, underscores or dots.
// // Unlike in Go identifiers, all digits are fine (e.g., "386").
// for _, c := range name {
// if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
// return false
// }
// }
//
for _, c := range t.Name() {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
return fmt.Errorf("character '%c' disallowed in tags", c)
}
}
return nil
}
// Evaluate the term under the given set of identifier values.
func (t Term) Evaluate(v map[string]bool) bool {
return (t.Validate() == nil) && (v[t.Name()] == !t.IsNegated())
}
// GoString returns t.
func (t Term) GoString() string { return string(t) }
// SetTags builds a set where the given list of identifiers are true.
func SetTags(idents ...string) map[string]bool {
v := map[string]bool{}
for _, ident := range idents {
v[ident] = true
}
return v
}
-253
View File
@@ -1,253 +0,0 @@
package gotypes
import (
"errors"
"fmt"
"go/token"
"go/types"
"strconv"
"github.com/mmcloughlin/avo/reg"
"github.com/mmcloughlin/avo/operand"
)
// Sizes provides type sizes used by the standard Go compiler on amd64.
var Sizes = types.SizesFor("gc", "amd64")
// Basic represents a primitive/basic type at a given memory address.
type Basic struct {
Addr operand.Mem
Type *types.Basic
}
// Component provides access to sub-components of a Go type.
type Component interface {
// When the component has no further sub-components, Resolve will return a
// reference to the components type and memory address. If there was an error
// during any previous calls to Component methods, they will be returned at
// resolution time.
Resolve() (*Basic, error)
Dereference(r reg.Register) Component // dereference a pointer
Base() Component // base pointer of a string or slice
Len() Component // length of a string or slice
Cap() Component // capacity of a slice
Real() Component // real part of a complex value
Imag() Component // imaginary part of a complex value
Index(int) Component // index into an array
Field(string) Component // access a struct field
}
// componenterr is an error that also provides a null implementation of the
// Component interface. This enables us to return an error from Component
// methods whilst also allowing method chaining to continue.
type componenterr string
func errorf(format string, args ...interface{}) Component {
return componenterr(fmt.Sprintf(format, args...))
}
func (c componenterr) Error() string { return string(c) }
func (c componenterr) Resolve() (*Basic, error) { return nil, c }
func (c componenterr) Dereference(r reg.Register) Component { return c }
func (c componenterr) Base() Component { return c }
func (c componenterr) Len() Component { return c }
func (c componenterr) Cap() Component { return c }
func (c componenterr) Real() Component { return c }
func (c componenterr) Imag() Component { return c }
func (c componenterr) Index(int) Component { return c }
func (c componenterr) Field(string) Component { return c }
type component struct {
typ types.Type
addr operand.Mem
}
// NewComponent builds a component for the named type at the given address.
func NewComponent(t types.Type, addr operand.Mem) Component {
return &component{
typ: t,
addr: addr,
}
}
func (c *component) Resolve() (*Basic, error) {
b := toprimitive(c.typ)
if b == nil {
return nil, errors.New("component is not primitive")
}
return &Basic{
Addr: c.addr,
Type: b,
}, nil
}
func (c *component) Dereference(r reg.Register) Component {
p, ok := c.typ.Underlying().(*types.Pointer)
if !ok {
return errorf("not pointer type")
}
return NewComponent(p.Elem(), operand.Mem{Base: r})
}
// Reference: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/reflect/value.go#L1800-L1804
//
// type SliceHeader struct {
// Data uintptr
// Len int
// Cap int
// }
//
var slicehdroffsets = Sizes.Offsetsof([]*types.Var{
types.NewField(token.NoPos, nil, "Data", types.Typ[types.Uintptr], false),
types.NewField(token.NoPos, nil, "Len", types.Typ[types.Int], false),
types.NewField(token.NoPos, nil, "Cap", types.Typ[types.Int], false),
})
func (c *component) Base() Component {
if !isslice(c.typ) && !isstring(c.typ) {
return errorf("only slices and strings have base pointers")
}
return c.sub("_base", int(slicehdroffsets[0]), types.Typ[types.Uintptr])
}
func (c *component) Len() Component {
if !isslice(c.typ) && !isstring(c.typ) {
return errorf("only slices and strings have length fields")
}
return c.sub("_len", int(slicehdroffsets[1]), types.Typ[types.Int])
}
func (c *component) Cap() Component {
if !isslice(c.typ) {
return errorf("only slices have capacity fields")
}
return c.sub("_cap", int(slicehdroffsets[2]), types.Typ[types.Int])
}
func (c *component) Real() Component {
if !iscomplex(c.typ) {
return errorf("only complex types have real values")
}
f := complextofloat(c.typ)
return c.sub("_real", 0, f)
}
func (c *component) Imag() Component {
if !iscomplex(c.typ) {
return errorf("only complex types have imaginary values")
}
f := complextofloat(c.typ)
return c.sub("_imag", int(Sizes.Sizeof(f)), f)
}
func (c *component) Index(i int) Component {
a, ok := c.typ.Underlying().(*types.Array)
if !ok {
return errorf("not array type")
}
if int64(i) >= a.Len() {
return errorf("array index out of bounds")
}
// Reference: https://github.com/golang/tools/blob/bcd4e47d02889ebbc25c9f4bf3d27e4124b0bf9d/go/analysis/passes/asmdecl/asmdecl.go#L482-L494
//
// case asmArray:
// tu := t.Underlying().(*types.Array)
// elem := tu.Elem()
// // Calculate offset of each element array.
// fields := []*types.Var{
// types.NewVar(token.NoPos, nil, "fake0", elem),
// types.NewVar(token.NoPos, nil, "fake1", elem),
// }
// offsets := arch.sizes.Offsetsof(fields)
// elemoff := int(offsets[1])
// for i := 0; i < int(tu.Len()); i++ {
// cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
// }
//
elem := a.Elem()
elemsize := int(Sizes.Sizeof(types.NewArray(elem, 2)) - Sizes.Sizeof(types.NewArray(elem, 1)))
return c.sub("_"+strconv.Itoa(i), i*elemsize, elem)
}
func (c *component) Field(n string) Component {
s, ok := c.typ.Underlying().(*types.Struct)
if !ok {
return errorf("not struct type")
}
// Reference: https://github.com/golang/tools/blob/13ba8ad772dfbf0f451b5dd0679e9c5605afc05d/go/analysis/passes/asmdecl/asmdecl.go#L471-L480
//
// case asmStruct:
// tu := t.Underlying().(*types.Struct)
// fields := make([]*types.Var, tu.NumFields())
// for i := 0; i < tu.NumFields(); i++ {
// fields[i] = tu.Field(i)
// }
// offsets := arch.sizes.Offsetsof(fields)
// for i, f := range fields {
// cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
// }
//
fields := make([]*types.Var, s.NumFields())
for i := 0; i < s.NumFields(); i++ {
fields[i] = s.Field(i)
}
offsets := Sizes.Offsetsof(fields)
for i, f := range fields {
if f.Name() == n {
return c.sub("_"+n, int(offsets[i]), f.Type())
}
}
return errorf("struct does not have field '%s'", n)
}
func (c *component) sub(suffix string, offset int, t types.Type) *component {
s := *c
if s.addr.Symbol.Name != "" {
s.addr.Symbol.Name += suffix
}
s.addr = s.addr.Offset(offset)
s.typ = t
return &s
}
func isslice(t types.Type) bool {
_, ok := t.Underlying().(*types.Slice)
return ok
}
func isstring(t types.Type) bool {
b, ok := t.Underlying().(*types.Basic)
return ok && b.Kind() == types.String
}
func iscomplex(t types.Type) bool {
b, ok := t.Underlying().(*types.Basic)
return ok && (b.Info()&types.IsComplex) != 0
}
func complextofloat(t types.Type) types.Type {
switch Sizes.Sizeof(t) {
case 16:
return types.Typ[types.Float64]
case 8:
return types.Typ[types.Float32]
}
panic("bad")
}
// toprimitive determines whether t is primitive (cannot be reduced into
// components). If it is, it returns the basic type for t, otherwise returns
// nil.
func toprimitive(t types.Type) *types.Basic {
switch b := t.(type) {
case *types.Basic:
if (b.Info() & (types.IsString | types.IsComplex)) == 0 {
return b
}
case *types.Pointer:
return types.Typ[types.Uintptr]
}
return nil
}
-2
View File
@@ -1,2 +0,0 @@
// Package gotypes provides helpers for interacting with Go types within avo functions.
package gotypes
-177
View File
@@ -1,177 +0,0 @@
package gotypes
import (
"bytes"
"errors"
"fmt"
"go/token"
"go/types"
"strconv"
"github.com/mmcloughlin/avo/operand"
)
// Signature represents a Go function signature.
type Signature struct {
pkg *types.Package
sig *types.Signature
params *Tuple
results *Tuple
}
// NewSignature constructs a Signature.
func NewSignature(pkg *types.Package, sig *types.Signature) *Signature {
s := &Signature{
pkg: pkg,
sig: sig,
}
s.init()
return s
}
// NewSignatureVoid builds the void signature "func()".
func NewSignatureVoid() *Signature {
return NewSignature(nil, types.NewSignature(nil, nil, nil, false))
}
// LookupSignature returns the signature of the named function in the provided package.
func LookupSignature(pkg *types.Package, name string) (*Signature, error) {
scope := pkg.Scope()
obj := scope.Lookup(name)
if obj == nil {
return nil, fmt.Errorf("could not find function \"%s\"", name)
}
s, ok := obj.Type().(*types.Signature)
if !ok {
return nil, fmt.Errorf("object \"%s\" does not have signature type", name)
}
return NewSignature(pkg, s), nil
}
// ParseSignature builds a Signature by parsing a Go function type expression.
// The function type must reference builtin types only; see
// ParseSignatureInPackage if custom types are required.
func ParseSignature(expr string) (*Signature, error) {
return ParseSignatureInPackage(nil, expr)
}
// ParseSignatureInPackage builds a Signature by parsing a Go function type
// expression. The expression may reference types in the provided package.
func ParseSignatureInPackage(pkg *types.Package, expr string) (*Signature, error) {
tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, expr)
if err != nil {
return nil, err
}
if tv.Value != nil {
return nil, errors.New("signature expression should have nil value")
}
s, ok := tv.Type.(*types.Signature)
if !ok {
return nil, errors.New("provided type is not a function signature")
}
return NewSignature(pkg, s), nil
}
// Params returns the function signature argument types.
func (s *Signature) Params() *Tuple { return s.params }
// Results returns the function return types.
func (s *Signature) Results() *Tuple { return s.results }
// Bytes returns the total size of the function arguments and return values.
func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes() }
// String writes Signature as a string. This does not include the "func" keyword.
func (s *Signature) String() string {
var buf bytes.Buffer
types.WriteSignature(&buf, s.sig, func(pkg *types.Package) string {
if pkg == s.pkg {
return ""
}
return pkg.Name()
})
return buf.String()
}
func (s *Signature) init() {
p := s.sig.Params()
r := s.sig.Results()
// Compute parameter offsets.
vs := tuplevars(p)
vs = append(vs, types.NewParam(token.NoPos, nil, "sentinel", types.Typ[types.Uint64]))
paramsoffsets := Sizes.Offsetsof(vs)
paramssize := paramsoffsets[p.Len()]
s.params = newTuple(p, paramsoffsets, paramssize, "arg")
// Result offsets.
vs = tuplevars(r)
resultsoffsets := Sizes.Offsetsof(vs)
var resultssize int64
if n := len(vs); n > 0 {
resultssize = resultsoffsets[n-1] + Sizes.Sizeof(vs[n-1].Type())
}
for i := range resultsoffsets {
resultsoffsets[i] += paramssize
}
s.results = newTuple(r, resultsoffsets, resultssize, "ret")
}
// Tuple represents a tuple of variables, such as function arguments or results.
type Tuple struct {
components []Component
byname map[string]Component
size int
}
func newTuple(t *types.Tuple, offsets []int64, size int64, defaultprefix string) *Tuple {
tuple := &Tuple{
byname: map[string]Component{},
size: int(size),
}
for i := 0; i < t.Len(); i++ {
v := t.At(i)
name := v.Name()
if name == "" {
name = defaultprefix
if i > 0 {
name += strconv.Itoa(i)
}
}
addr := operand.NewParamAddr(name, int(offsets[i]))
c := NewComponent(v.Type(), addr)
tuple.components = append(tuple.components, c)
if v.Name() != "" {
tuple.byname[v.Name()] = c
}
}
return tuple
}
// Lookup returns the variable with the given name.
func (t *Tuple) Lookup(name string) Component {
e := t.byname[name]
if e == nil {
return errorf("unknown variable \"%s\"", name)
}
return e
}
// At returns the variable at index i.
func (t *Tuple) At(i int) Component {
if i >= len(t.components) {
return errorf("index out of range")
}
return t.components[i]
}
// Bytes returns the size of the Tuple. This may include additional padding.
func (t *Tuple) Bytes() int { return t.size }
func tuplevars(t *types.Tuple) []*types.Var {
vs := make([]*types.Var, t.Len())
for i := 0; i < t.Len(); i++ {
vs[i] = t.At(i)
}
return vs
}
-60
View File
@@ -1,60 +0,0 @@
// Package prnt provides common functionality for code generators.
package prnt
import (
"bytes"
"fmt"
"io"
"strings"
)
// Generator provides convenience methods for code generators. In particular it
// provides fmt-like methods which print to an internal buffer. It also allows
// any errors to be stored so they can be checked at the end, rather than having
// error checks obscuring the code generation.
type Generator struct {
buf bytes.Buffer
err error
}
// Raw provides direct access to the underlying output stream.
func (g *Generator) Raw() io.Writer {
return &g.buf
}
// Printf prints to the internal buffer.
func (g *Generator) Printf(format string, args ...interface{}) {
if g.err != nil {
return
}
_, err := fmt.Fprintf(&g.buf, format, args...)
g.AddError(err)
}
// NL prints a new line.
func (g *Generator) NL() {
g.Printf("\n")
}
// Comment writes comment lines prefixed with "// ".
func (g *Generator) Comment(lines ...string) {
for _, line := range lines {
line = strings.TrimSpace("// " + line)
g.Printf("%s\n", line)
}
}
// AddError records an error in code generation. The first non-nil error will
// prevent printing operations from writing anything else, and the error will be
// returned from Result().
func (g *Generator) AddError(err error) {
if err != nil && g.err == nil {
g.err = err
}
}
// Result returns the printed bytes. If any error was recorded with AddError
// during code generation, the first such error will be returned here.
func (g *Generator) Result() ([]byte, error) {
return g.buf.Bytes(), g.err
}
-73
View File
@@ -1,73 +0,0 @@
// Package stack provides helpers for querying the callstack.
package stack
import (
"path"
"runtime"
"strings"
)
// Frames returns at most max callstack Frames, starting with its caller and
// skipping skip Frames.
func Frames(skip, max int) []runtime.Frame {
pc := make([]uintptr, max)
n := runtime.Callers(skip+2, pc)
if n == 0 {
return nil
}
pc = pc[:n]
frames := runtime.CallersFrames(pc)
var fs []runtime.Frame
for {
f, more := frames.Next()
fs = append(fs, f)
if !more {
break
}
}
return fs
}
// Match returns the first stack frame for which the predicate function returns
// true. Returns nil if no match is found. Starts matching after skip frames,
// starting with its caller.
func Match(skip int, predicate func(runtime.Frame) bool) *runtime.Frame {
i, n := skip+1, 16
for {
fs := Frames(i, n)
for j, f := range fs {
if predicate(f) {
return &fs[j]
}
}
if len(fs) < n {
break
}
i += n
}
return nil
}
// Main returns the main() function Frame.
func Main() *runtime.Frame {
return Match(1, func(f runtime.Frame) bool {
return f.Function == "main.main"
})
}
// ExternalCaller returns the first frame outside the callers package.
func ExternalCaller() *runtime.Frame {
var first *runtime.Frame
return Match(1, func(f runtime.Frame) bool {
if first == nil {
first = &f
}
return pkg(first.Function) != pkg(f.Function)
})
}
func pkg(ident string) string {
dir, name := path.Split(ident)
parts := strings.Split(name, ".")
return dir + parts[0]
}
-2
View File
@@ -1,2 +0,0 @@
// Package ir provides the intermediate representation of avo programs.
package ir
-355
View File
@@ -1,355 +0,0 @@
package ir
import (
"errors"
"github.com/mmcloughlin/avo/attr"
"github.com/mmcloughlin/avo/buildtags"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
)
// Node is a part of a Function.
type Node interface {
node()
}
// Label within a function.
type Label string
func (l Label) node() {}
// Comment represents a multi-line comment.
type Comment struct {
Lines []string
}
func (c *Comment) node() {}
// NewComment builds a Comment consisting of the provided lines.
func NewComment(lines ...string) *Comment {
return &Comment{
Lines: lines,
}
}
// Instruction is a single instruction in a function.
type Instruction struct {
Opcode string
Operands []operand.Op
Inputs []operand.Op
Outputs []operand.Op
IsTerminal bool
IsBranch bool
IsConditional bool
CancellingInputs bool
// ISA is the list of required instruction set extensions.
ISA []string
// CFG.
Pred []*Instruction
Succ []*Instruction
// LiveIn/LiveOut are sets of live register IDs pre/post execution.
LiveIn reg.MaskSet
LiveOut reg.MaskSet
}
func (i *Instruction) node() {}
// IsUnconditionalBranch reports whether i is an unconditional branch.
func (i Instruction) IsUnconditionalBranch() bool {
return i.IsBranch && !i.IsConditional
}
// TargetLabel returns the label referenced by this instruction. Returns nil if
// no label is referenced.
func (i Instruction) TargetLabel() *Label {
if !i.IsBranch {
return nil
}
if len(i.Operands) == 0 {
return nil
}
if ref, ok := i.Operands[0].(operand.LabelRef); ok {
lbl := Label(ref)
return &lbl
}
return nil
}
// Registers returns all registers involved in the instruction.
func (i Instruction) Registers() []reg.Register {
var rs []reg.Register
for _, op := range i.Operands {
rs = append(rs, operand.Registers(op)...)
}
return rs
}
// InputRegisters returns all registers read by this instruction.
func (i Instruction) InputRegisters() []reg.Register {
var rs []reg.Register
for _, op := range i.Inputs {
rs = append(rs, operand.Registers(op)...)
}
if i.CancellingInputs && rs[0] == rs[1] {
rs = []reg.Register{}
}
for _, op := range i.Outputs {
if operand.IsMem(op) {
rs = append(rs, operand.Registers(op)...)
}
}
return rs
}
// OutputRegisters returns all registers written by this instruction.
func (i Instruction) OutputRegisters() []reg.Register {
var rs []reg.Register
for _, op := range i.Outputs {
if r, ok := op.(reg.Register); ok {
rs = append(rs, r)
}
}
return rs
}
// Section is a part of a file.
type Section interface {
section()
}
// File represents an assembly file.
type File struct {
Constraints buildtags.Constraints
Includes []string
Sections []Section
}
// NewFile initializes an empty file.
func NewFile() *File {
return &File{}
}
// AddSection appends a Section to the file.
func (f *File) AddSection(s Section) {
f.Sections = append(f.Sections, s)
}
// Functions returns all functions in the file.
func (f *File) Functions() []*Function {
var fns []*Function
for _, s := range f.Sections {
if fn, ok := s.(*Function); ok {
fns = append(fns, fn)
}
}
return fns
}
// Pragma represents a function compiler directive.
type Pragma struct {
Directive string
Arguments []string
}
// Function represents an assembly function.
type Function struct {
Name string
Attributes attr.Attribute
Pragmas []Pragma
Doc []string
Signature *gotypes.Signature
LocalSize int
Nodes []Node
// LabelTarget maps from label name to the following instruction.
LabelTarget map[Label]*Instruction
// Register allocation.
Allocation reg.Allocation
// ISA is the list of required instruction set extensions.
ISA []string
}
func (f *Function) section() {}
// NewFunction builds an empty function of the given name.
func NewFunction(name string) *Function {
return &Function{
Name: name,
Signature: gotypes.NewSignatureVoid(),
}
}
// AddPragma adds a pragma to this function.
func (f *Function) AddPragma(directive string, args ...string) {
f.Pragmas = append(f.Pragmas, Pragma{
Directive: directive,
Arguments: args,
})
}
// SetSignature sets the function signature.
func (f *Function) SetSignature(s *gotypes.Signature) {
f.Signature = s
}
// AllocLocal allocates size bytes in this function's stack.
// Returns a reference to the base pointer for the newly allocated region.
func (f *Function) AllocLocal(size int) operand.Mem {
ptr := operand.NewStackAddr(f.LocalSize)
f.LocalSize += size
return ptr
}
// AddInstruction appends an instruction to f.
func (f *Function) AddInstruction(i *Instruction) {
f.AddNode(i)
}
// AddLabel appends a label to f.
func (f *Function) AddLabel(l Label) {
f.AddNode(l)
}
// AddComment adds comment lines to f.
func (f *Function) AddComment(lines ...string) {
f.AddNode(NewComment(lines...))
}
// AddNode appends a Node to f.
func (f *Function) AddNode(n Node) {
f.Nodes = append(f.Nodes, n)
}
// Instructions returns just the list of instruction nodes.
func (f *Function) Instructions() []*Instruction {
var is []*Instruction
for _, n := range f.Nodes {
i, ok := n.(*Instruction)
if ok {
is = append(is, i)
}
}
return is
}
// Labels returns just the list of label nodes.
func (f *Function) Labels() []Label {
var lbls []Label
for _, n := range f.Nodes {
lbl, ok := n.(Label)
if ok {
lbls = append(lbls, lbl)
}
}
return lbls
}
// Stub returns the Go function declaration.
func (f *Function) Stub() string {
return "func " + f.Name + f.Signature.String()
}
// FrameBytes returns the size of the stack frame in bytes.
func (f *Function) FrameBytes() int {
return f.LocalSize
}
// ArgumentBytes returns the size of the arguments in bytes.
func (f *Function) ArgumentBytes() int {
return f.Signature.Bytes()
}
// Datum represents a data element at a particular offset of a data section.
type Datum struct {
Offset int
Value operand.Constant
}
// NewDatum builds a Datum from the given constant.
func NewDatum(offset int, v operand.Constant) Datum {
return Datum{
Offset: offset,
Value: v,
}
}
// Interval returns the range of bytes this datum will occupy within its section.
func (d Datum) Interval() (int, int) {
return d.Offset, d.Offset + d.Value.Bytes()
}
// Overlaps returns true
func (d Datum) Overlaps(other Datum) bool {
s, e := d.Interval()
so, eo := other.Interval()
return !(eo <= s || e <= so)
}
// Global represents a DATA section.
type Global struct {
Symbol operand.Symbol
Attributes attr.Attribute
Data []Datum
Size int
}
// NewGlobal constructs an empty DATA section.
func NewGlobal(sym operand.Symbol) *Global {
return &Global{
Symbol: sym,
}
}
// NewStaticGlobal is a convenience for building a static DATA section.
func NewStaticGlobal(name string) *Global {
return NewGlobal(operand.NewStaticSymbol(name))
}
func (g *Global) section() {}
// Base returns a pointer to the start of the data section.
func (g *Global) Base() operand.Mem {
return operand.NewDataAddr(g.Symbol, 0)
}
// Grow ensures that the data section has at least the given size.
func (g *Global) Grow(size int) {
if g.Size < size {
g.Size = size
}
}
// AddDatum adds d to this data section, growing it if necessary. Errors if the datum overlaps with existing data.
func (g *Global) AddDatum(d Datum) error {
for _, other := range g.Data {
if d.Overlaps(other) {
return errors.New("overlaps existing datum")
}
}
g.add(d)
return nil
}
// Append the constant to the end of the data section.
func (g *Global) Append(v operand.Constant) {
g.add(Datum{
Offset: g.Size,
Value: v,
})
}
func (g *Global) add(d Datum) {
_, end := d.Interval()
g.Grow(end)
g.Data = append(g.Data, d)
}
-247
View File
@@ -1,247 +0,0 @@
package operand
import "github.com/mmcloughlin/avo/reg"
// Pure type assertion checks:
// IsRegister returns whether op has type reg.Register.
func IsRegister(op Op) bool { _, ok := op.(reg.Register); return ok }
// IsMem returns whether op has type Mem.
func IsMem(op Op) bool { _, ok := op.(Mem); return ok }
// IsRel returns whether op has type Rel.
func IsRel(op Op) bool { _, ok := op.(Rel); return ok }
// Checks corresponding to specific operand types in the Intel Manual:
// Is1 returns true if op is the immediate constant 1.
func Is1(op Op) bool {
i, ok := op.(U8)
return ok && i == 1
}
// Is3 returns true if op is the immediate constant 3.
func Is3(op Op) bool {
i, ok := op.(U8)
return ok && i == 3
}
// IsIMM2U returns true if op is a 2-bit unsigned immediate (less than 4).
func IsIMM2U(op Op) bool {
i, ok := op.(U8)
return ok && i < 4
}
// IsIMM8 returns true is op is an 8-bit immediate.
func IsIMM8(op Op) bool {
_, ok := op.(U8)
return ok
}
// IsIMM16 returns true is op is a 16-bit immediate.
func IsIMM16(op Op) bool {
_, ok := op.(U16)
return ok
}
// IsIMM32 returns true is op is a 32-bit immediate.
func IsIMM32(op Op) bool {
_, ok := op.(U32)
return ok
}
// IsIMM64 returns true is op is a 64-bit immediate.
func IsIMM64(op Op) bool {
_, ok := op.(U64)
return ok
}
// IsAL returns true if op is the AL register.
func IsAL(op Op) bool {
return op == reg.AL
}
// IsCL returns true if op is the CL register.
func IsCL(op Op) bool {
return op == reg.CL
}
// IsAX returns true if op is the 16-bit AX register.
func IsAX(op Op) bool {
return op == reg.AX
}
// IsEAX returns true if op is the 32-bit EAX register.
func IsEAX(op Op) bool {
return op == reg.EAX
}
// IsRAX returns true if op is the 64-bit RAX register.
func IsRAX(op Op) bool {
return op == reg.RAX
}
// IsR8 returns true if op is an 8-bit general-purpose register.
func IsR8(op Op) bool {
return IsGP(op, 1)
}
// IsR16 returns true if op is a 16-bit general-purpose register.
func IsR16(op Op) bool {
return IsGP(op, 2)
}
// IsR32 returns true if op is a 32-bit general-purpose register.
func IsR32(op Op) bool {
return IsGP(op, 4)
}
// IsR64 returns true if op is a 64-bit general-purpose register.
func IsR64(op Op) bool {
return IsGP(op, 8)
}
// IsPseudo returns true if op is a pseudo register.
func IsPseudo(op Op) bool {
return IsRegisterKind(op, reg.KindPseudo)
}
// IsGP returns true if op is a general-purpose register of size n bytes.
func IsGP(op Op, n uint) bool {
return IsRegisterKindSize(op, reg.KindGP, n)
}
// IsXMM0 returns true if op is the X0 register.
func IsXMM0(op Op) bool {
return op == reg.X0
}
// IsXMM returns true if op is a 128-bit XMM register.
func IsXMM(op Op) bool {
return IsRegisterKindSize(op, reg.KindVector, 16)
}
// IsYMM returns true if op is a 256-bit YMM register.
func IsYMM(op Op) bool {
return IsRegisterKindSize(op, reg.KindVector, 32)
}
// IsRegisterKindSize returns true if op is a register of the given kind and size in bytes.
func IsRegisterKindSize(op Op, k reg.Kind, n uint) bool {
r, ok := op.(reg.Register)
return ok && r.Kind() == k && r.Size() == n
}
// IsRegisterKind returns true if op is a register of the given kind.
func IsRegisterKind(op Op, k reg.Kind) bool {
r, ok := op.(reg.Register)
return ok && r.Kind() == k
}
// IsM returns true if op is a 16-, 32- or 64-bit memory operand.
func IsM(op Op) bool {
// TODO(mbm): confirm "m" check is defined correctly
// Intel manual: "A 16-, 32- or 64-bit operand in memory."
return IsM16(op) || IsM32(op) || IsM64(op)
}
// IsM8 returns true if op is an 8-bit memory operand.
func IsM8(op Op) bool {
// TODO(mbm): confirm "m8" check is defined correctly
// Intel manual: "A byte operand in memory, usually expressed as a variable or
// array name, but pointed to by the DS:(E)SI or ES:(E)DI registers. In 64-bit
// mode, it is pointed to by the RSI or RDI registers."
return IsMSize(op, 1)
}
// IsM16 returns true if op is a 16-bit memory operand.
func IsM16(op Op) bool {
return IsMSize(op, 2)
}
// IsM32 returns true if op is a 16-bit memory operand.
func IsM32(op Op) bool {
return IsMSize(op, 4)
}
// IsM64 returns true if op is a 64-bit memory operand.
func IsM64(op Op) bool {
return IsMSize(op, 8)
}
// IsMSize returns true if op is a memory operand using general-purpose address
// registers of the given size in bytes.
func IsMSize(op Op, n uint) bool {
// TODO(mbm): should memory operands have a size attribute as well?
// TODO(mbm): m8,m16,m32,m64 checks do not actually check size
m, ok := op.(Mem)
return ok && IsMReg(m.Base) && (m.Index == nil || IsMReg(m.Index))
}
// IsMReg returns true if op is a register that can be used in a memory operand.
func IsMReg(op Op) bool {
return IsPseudo(op) || IsRegisterKind(op, reg.KindGP)
}
// IsM128 returns true if op is a 128-bit memory operand.
func IsM128(op Op) bool {
// TODO(mbm): should "m128" be the same as "m64"?
return IsM64(op)
}
// IsM256 returns true if op is a 256-bit memory operand.
func IsM256(op Op) bool {
// TODO(mbm): should "m256" be the same as "m64"?
return IsM64(op)
}
// IsVM32X returns true if op is a vector memory operand with 32-bit XMM index.
func IsVM32X(op Op) bool {
return IsVmx(op)
}
// IsVM64X returns true if op is a vector memory operand with 64-bit XMM index.
func IsVM64X(op Op) bool {
return IsVmx(op)
}
// IsVmx returns true if op is a vector memory operand with XMM index.
func IsVmx(op Op) bool {
return isvm(op, IsXMM)
}
// IsVM32Y returns true if op is a vector memory operand with 32-bit YMM index.
func IsVM32Y(op Op) bool {
return IsVmy(op)
}
// IsVM64Y returns true if op is a vector memory operand with 64-bit YMM index.
func IsVM64Y(op Op) bool {
return IsVmy(op)
}
// IsVmy returns true if op is a vector memory operand with YMM index.
func IsVmy(op Op) bool {
return isvm(op, IsYMM)
}
func isvm(op Op, idx func(Op) bool) bool {
m, ok := op.(Mem)
return ok && IsR64(m.Base) && idx(m.Index)
}
// IsREL8 returns true if op is an 8-bit offset relative to instruction pointer.
func IsREL8(op Op) bool {
r, ok := op.(Rel)
return ok && r == Rel(int8(r))
}
// IsREL32 returns true if op is an offset relative to instruction pointer, or a
// label reference.
func IsREL32(op Op) bool {
// TODO(mbm): should labels be considered separately?
_, rel := op.(Rel)
_, label := op.(LabelRef)
return rel || label
}
-36
View File
@@ -1,36 +0,0 @@
package operand
import "fmt"
// Constant represents a constant literal.
type Constant interface {
Op
Bytes() int
constant()
}
//go:generate go run make_const.go -output zconst.go
// String is a string constant.
type String string
// Asm returns an assembly syntax representation of the string s.
func (s String) Asm() string { return fmt.Sprintf("$%q", s) }
// Bytes returns the length of s.
func (s String) Bytes() int { return len(s) }
func (s String) constant() {}
// Imm returns an unsigned integer constant with size guessed from x.
func Imm(x uint64) Constant {
switch {
case uint64(uint8(x)) == x:
return U8(x)
case uint64(uint16(x)) == x:
return U16(x)
case uint64(uint32(x)) == x:
return U32(x)
}
return U64(x)
}
-2
View File
@@ -1,2 +0,0 @@
// Package operand provides types for instruction operands.
package operand
-151
View File
@@ -1,151 +0,0 @@
package operand
import (
"fmt"
"github.com/mmcloughlin/avo/reg"
)
// Op is an operand.
type Op interface {
Asm() string
}
// Symbol represents a symbol name.
type Symbol struct {
Name string
Static bool // only visible in current source file
}
// NewStaticSymbol builds a static Symbol. Static symbols are only visible in the current source file.
func NewStaticSymbol(name string) Symbol {
return Symbol{Name: name, Static: true}
}
func (s Symbol) String() string {
n := s.Name
if s.Static {
n += "<>"
}
return n
}
// Mem represents a memory reference.
type Mem struct {
Symbol Symbol
Disp int
Base reg.Register
Index reg.Register
Scale uint8
}
// NewParamAddr is a convenience to build a Mem operand pointing to a function
// parameter, which is a named offset from the frame pointer pseudo register.
func NewParamAddr(name string, offset int) Mem {
return Mem{
Symbol: Symbol{
Name: name,
Static: false,
},
Disp: offset,
Base: reg.FramePointer,
}
}
// NewStackAddr returns a memory reference relative to the stack pointer.
func NewStackAddr(offset int) Mem {
return Mem{
Disp: offset,
Base: reg.StackPointer,
}
}
// NewDataAddr returns a memory reference relative to the named data symbol.
func NewDataAddr(sym Symbol, offset int) Mem {
return Mem{
Symbol: sym,
Disp: offset,
Base: reg.StaticBase,
}
}
// Offset returns a reference to m plus idx bytes.
func (m Mem) Offset(idx int) Mem {
a := m
a.Disp += idx
return a
}
// Idx returns a new memory reference with (Index, Scale) set to (r, s).
func (m Mem) Idx(r reg.Register, s uint8) Mem {
a := m
a.Index = r
a.Scale = s
return a
}
// Asm returns an assembly syntax representation of m.
func (m Mem) Asm() string {
a := m.Symbol.String()
if a != "" {
a += fmt.Sprintf("%+d", m.Disp)
} else if m.Disp != 0 {
a += fmt.Sprintf("%d", m.Disp)
}
if m.Base != nil {
a += fmt.Sprintf("(%s)", m.Base.Asm())
}
if m.Index != nil && m.Scale != 0 {
a += fmt.Sprintf("(%s*%d)", m.Index.Asm(), m.Scale)
}
return a
}
// Rel is an offset relative to the instruction pointer.
type Rel int32
// Asm returns an assembly syntax representation of r.
func (r Rel) Asm() string {
return fmt.Sprintf(".%+d", r)
}
// LabelRef is a reference to a label.
type LabelRef string
// Asm returns an assembly syntax representation of l.
func (l LabelRef) Asm() string {
return string(l)
}
// Registers returns the list of all operands involved in the given operand.
func Registers(op Op) []reg.Register {
switch op := op.(type) {
case reg.Register:
return []reg.Register{op}
case Mem:
var r []reg.Register
if op.Base != nil {
r = append(r, op.Base)
}
if op.Index != nil {
r = append(r, op.Index)
}
return r
case Constant, Rel, LabelRef:
return nil
}
panic("unknown operand type")
}
// ApplyAllocation returns an operand with allocated registers replaced. Registers missing from the allocation are left alone.
func ApplyAllocation(op Op, a reg.Allocation) Op {
switch op := op.(type) {
case reg.Register:
return a.LookupRegisterDefault(op)
case Mem:
op.Base = a.LookupRegisterDefault(op.Base)
op.Index = a.LookupRegisterDefault(op.Index)
return op
}
return op
}
-75
View File
@@ -1,75 +0,0 @@
// Code generated by make_const.go. DO NOT EDIT.
package operand
import "fmt"
// I8 is a 8-bit signed integer constant.
type I8 int8
func (i I8) Asm() string { return fmt.Sprintf("$%+d", i) }
func (i I8) Bytes() int { return 1 }
func (i I8) constant() {}
// U8 is a 8-bit unsigned integer constant.
type U8 uint8
func (u U8) Asm() string { return fmt.Sprintf("$%#02x", u) }
func (u U8) Bytes() int { return 1 }
func (u U8) constant() {}
// I16 is a 16-bit signed integer constant.
type I16 int16
func (i I16) Asm() string { return fmt.Sprintf("$%+d", i) }
func (i I16) Bytes() int { return 2 }
func (i I16) constant() {}
// U16 is a 16-bit unsigned integer constant.
type U16 uint16
func (u U16) Asm() string { return fmt.Sprintf("$%#04x", u) }
func (u U16) Bytes() int { return 2 }
func (u U16) constant() {}
// F32 is a 32-bit floating point constant.
type F32 float32
func (f F32) Asm() string { return fmt.Sprintf("$(%#v)", f) }
func (f F32) Bytes() int { return 4 }
func (f F32) constant() {}
// I32 is a 32-bit signed integer constant.
type I32 int32
func (i I32) Asm() string { return fmt.Sprintf("$%+d", i) }
func (i I32) Bytes() int { return 4 }
func (i I32) constant() {}
// U32 is a 32-bit unsigned integer constant.
type U32 uint32
func (u U32) Asm() string { return fmt.Sprintf("$%#08x", u) }
func (u U32) Bytes() int { return 4 }
func (u U32) constant() {}
// F64 is a 64-bit floating point constant.
type F64 float64
func (f F64) Asm() string { return fmt.Sprintf("$(%#v)", f) }
func (f F64) Bytes() int { return 8 }
func (f F64) constant() {}
// I64 is a 64-bit signed integer constant.
type I64 int64
func (i I64) Asm() string { return fmt.Sprintf("$%+d", i) }
func (i I64) Bytes() int { return 8 }
func (i I64) constant() {}
// U64 is a 64-bit unsigned integer constant.
type U64 uint64
func (u U64) Asm() string { return fmt.Sprintf("$%#016x", u) }
func (u U64) Bytes() int { return 8 }
func (u U64) constant() {}
-190
View File
@@ -1,190 +0,0 @@
package pass
import (
"errors"
"math"
"sort"
"github.com/mmcloughlin/avo/reg"
)
// edge is an edge of the interference graph, indicating that registers X and Y
// must be in non-conflicting registers.
type edge struct {
X, Y reg.ID
}
// Allocator is a graph-coloring register allocator.
type Allocator struct {
registers []reg.ID
allocation reg.Allocation
edges []*edge
possible map[reg.ID][]reg.ID
}
// NewAllocator builds an allocator for the given physical registers.
func NewAllocator(rs []reg.Physical) (*Allocator, error) {
// Set of IDs, excluding restricted registers.
idset := map[reg.ID]bool{}
for _, r := range rs {
if (r.Info() & reg.Restricted) != 0 {
continue
}
idset[r.ID()] = true
}
if len(idset) == 0 {
return nil, errors.New("no allocatable registers")
}
// Produce slice of unique register IDs.
var ids []reg.ID
for id := range idset {
ids = append(ids, id)
}
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
return &Allocator{
registers: ids,
allocation: reg.NewEmptyAllocation(),
possible: map[reg.ID][]reg.ID{},
}, nil
}
// NewAllocatorForKind builds an allocator for the given kind of registers.
func NewAllocatorForKind(k reg.Kind) (*Allocator, error) {
f := reg.FamilyOfKind(k)
if f == nil {
return nil, errors.New("unknown register family")
}
return NewAllocator(f.Registers())
}
// AddInterferenceSet records that r interferes with every register in s. Convenience wrapper around AddInterference.
func (a *Allocator) AddInterferenceSet(r reg.Register, s reg.MaskSet) {
for id, mask := range s {
if (r.Mask() & mask) != 0 {
a.AddInterference(r.ID(), id)
}
}
}
// AddInterference records that x and y must be assigned to non-conflicting physical registers.
func (a *Allocator) AddInterference(x, y reg.ID) {
a.Add(x)
a.Add(y)
a.edges = append(a.edges, &edge{X: x, Y: y})
}
// Add adds a register to be allocated. Does nothing if the register has already been added.
func (a *Allocator) Add(v reg.ID) {
if !v.IsVirtual() {
return
}
if _, found := a.possible[v]; found {
return
}
a.possible[v] = a.possibleregisters(v)
}
// Allocate allocates physical registers.
func (a *Allocator) Allocate() (reg.Allocation, error) {
for {
if err := a.update(); err != nil {
return nil, err
}
if a.remaining() == 0 {
break
}
v := a.mostrestricted()
if err := a.alloc(v); err != nil {
return nil, err
}
}
return a.allocation, nil
}
// update possible allocations based on edges.
func (a *Allocator) update() error {
var rem []*edge
for _, e := range a.edges {
x := a.allocation.LookupDefault(e.X)
y := a.allocation.LookupDefault(e.Y)
switch {
case x.IsVirtual() && y.IsVirtual():
rem = append(rem, e)
continue
case x.IsPhysical() && y.IsPhysical():
if x == y {
return errors.New("impossible register allocation")
}
case x.IsPhysical() && y.IsVirtual():
a.discardconflicting(y, x)
case x.IsVirtual() && y.IsPhysical():
a.discardconflicting(x, y)
default:
panic("unreachable")
}
}
a.edges = rem
return nil
}
// mostrestricted returns the virtual register with the least possibilities.
func (a *Allocator) mostrestricted() reg.ID {
n := int(math.MaxInt32)
var v reg.ID
for w, p := range a.possible {
// On a tie, choose the smallest ID in numeric order. This avoids
// non-deterministic allocations due to map iteration order.
if len(p) < n || (len(p) == n && w < v) {
n = len(p)
v = w
}
}
return v
}
// discardconflicting removes registers from vs possible list that conflict with p.
func (a *Allocator) discardconflicting(v, p reg.ID) {
a.possible[v] = filterregisters(a.possible[v], func(r reg.ID) bool {
return r != p
})
}
// alloc attempts to allocate a register to v.
func (a *Allocator) alloc(v reg.ID) error {
ps := a.possible[v]
if len(ps) == 0 {
return errors.New("failed to allocate registers")
}
p := ps[0]
a.allocation[v] = p
delete(a.possible, v)
return nil
}
// remaining returns the number of unallocated registers.
func (a *Allocator) remaining() int {
return len(a.possible)
}
// possibleregisters returns all allocate-able registers for the given virtual.
func (a *Allocator) possibleregisters(v reg.ID) []reg.ID {
return filterregisters(a.registers, func(r reg.ID) bool {
return v.Kind() == r.Kind()
})
}
func filterregisters(in []reg.ID, predicate func(reg.ID) bool) []reg.ID {
var rs []reg.ID
for _, r := range in {
if predicate(r) {
rs = append(rs, r)
}
}
return rs
}
-81
View File
@@ -1,81 +0,0 @@
package pass
import (
"errors"
"fmt"
"github.com/mmcloughlin/avo/ir"
)
// LabelTarget populates the LabelTarget of the given function. This maps from
// label name to the following instruction.
func LabelTarget(fn *ir.Function) error {
target := map[ir.Label]*ir.Instruction{}
var pending []ir.Label
for _, node := range fn.Nodes {
switch n := node.(type) {
case ir.Label:
if _, found := target[n]; found {
return fmt.Errorf("duplicate label \"%s\"", n)
}
pending = append(pending, n)
case *ir.Instruction:
for _, label := range pending {
target[label] = n
}
pending = nil
}
}
if len(pending) != 0 {
return errors.New("function ends with label")
}
fn.LabelTarget = target
return nil
}
// CFG constructs the call-flow-graph for the function.
func CFG(fn *ir.Function) error {
is := fn.Instructions()
n := len(is)
// Populate successors.
for i := 0; i < n; i++ {
cur := is[i]
var nxt *ir.Instruction
if i+1 < n {
nxt = is[i+1]
}
// If it's a branch, locate the target.
if cur.IsBranch {
lbl := cur.TargetLabel()
if lbl == nil {
return errors.New("no label for branch instruction")
}
target, found := fn.LabelTarget[*lbl]
if !found {
return fmt.Errorf("unknown label %q", *lbl)
}
cur.Succ = append(cur.Succ, target)
}
// Otherwise, could continue to the following instruction.
switch {
case cur.IsTerminal:
case cur.IsUnconditionalBranch():
default:
cur.Succ = append(cur.Succ, nxt)
}
}
// Populate predecessors.
for _, i := range is {
for _, s := range i.Succ {
if s != nil {
s.Pred = append(s.Pred, i)
}
}
}
return nil
}
-123
View File
@@ -1,123 +0,0 @@
package pass
import (
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
)
// PruneJumpToFollowingLabel removes jump instructions that target an
// immediately following label.
func PruneJumpToFollowingLabel(fn *ir.Function) error {
for i := 0; i+1 < len(fn.Nodes); i++ {
node := fn.Nodes[i]
next := fn.Nodes[i+1]
// This node is an unconditional jump.
inst, ok := node.(*ir.Instruction)
if !ok || !inst.IsBranch || inst.IsConditional {
continue
}
target := inst.TargetLabel()
if target == nil {
continue
}
// And the jump target is the immediately following node.
lbl, ok := next.(ir.Label)
if !ok || lbl != *target {
continue
}
// Then the jump is unnecessary and can be removed.
fn.Nodes = deletenode(fn.Nodes, i)
i--
}
return nil
}
// PruneDanglingLabels removes labels that are not referenced by any branches.
func PruneDanglingLabels(fn *ir.Function) error {
// Count label references.
count := map[ir.Label]int{}
for _, n := range fn.Nodes {
i, ok := n.(*ir.Instruction)
if !ok || !i.IsBranch {
continue
}
target := i.TargetLabel()
if target == nil {
continue
}
count[*target]++
}
// Look for labels with no references.
for i := 0; i < len(fn.Nodes); i++ {
node := fn.Nodes[i]
lbl, ok := node.(ir.Label)
if !ok {
continue
}
if count[lbl] == 0 {
fn.Nodes = deletenode(fn.Nodes, i)
i--
}
}
return nil
}
// PruneSelfMoves removes move instructions from one register to itself.
func PruneSelfMoves(fn *ir.Function) error {
return removeinstructions(fn, func(i *ir.Instruction) bool {
switch i.Opcode {
case "MOVB", "MOVW", "MOVL", "MOVQ":
default:
return false
}
return operand.IsRegister(i.Operands[0]) && operand.IsRegister(i.Operands[1]) && i.Operands[0] == i.Operands[1]
})
}
// removeinstructions deletes instructions from the given function which match predicate.
func removeinstructions(fn *ir.Function, predicate func(*ir.Instruction) bool) error {
// Removal of instructions has the potential to invalidate CFG structures.
// Clear them to prevent accidental use of stale structures after this pass.
invalidatecfg(fn)
for i := 0; i < len(fn.Nodes); i++ {
n := fn.Nodes[i]
inst, ok := n.(*ir.Instruction)
if !ok || !predicate(inst) {
continue
}
fn.Nodes = deletenode(fn.Nodes, i)
}
return nil
}
// deletenode deletes node i from nodes and returns the resulting slice.
func deletenode(nodes []ir.Node, i int) []ir.Node {
n := len(nodes)
copy(nodes[i:], nodes[i+1:])
nodes[n-1] = nil
return nodes[:n-1]
}
// invalidatecfg clears CFG structures.
func invalidatecfg(fn *ir.Function) {
fn.LabelTarget = nil
for _, i := range fn.Instructions() {
i.Pred = nil
i.Succ = nil
}
}
-31
View File
@@ -1,31 +0,0 @@
package pass
import (
"sort"
"github.com/mmcloughlin/avo/ir"
)
// RequiredISAExtensions determines ISA extensions required for the given
// function. Populates the ISA field.
func RequiredISAExtensions(fn *ir.Function) error {
// Collect ISA set.
set := map[string]bool{}
for _, i := range fn.Instructions() {
for _, isa := range i.ISA {
set[isa] = true
}
}
if len(set) == 0 {
return nil
}
// Populate the function's ISA field with the unique sorted list.
for isa := range set {
fn.ISA = append(fn.ISA, isa)
}
sort.Strings(fn.ISA)
return nil
}
-100
View File
@@ -1,100 +0,0 @@
// Package pass implements processing passes on avo Files.
package pass
import (
"io"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/printer"
)
// Compile pass compiles an avo file. Upon successful completion the avo file
// may be printed to Go assembly.
var Compile = Concat(
Verify,
FunctionPass(PruneJumpToFollowingLabel),
FunctionPass(PruneDanglingLabels),
FunctionPass(LabelTarget),
FunctionPass(CFG),
InstructionPass(ZeroExtend32BitOutputs),
FunctionPass(Liveness),
FunctionPass(AllocateRegisters),
FunctionPass(BindRegisters),
FunctionPass(VerifyAllocation),
Func(IncludeTextFlagHeader),
FunctionPass(PruneSelfMoves),
FunctionPass(RequiredISAExtensions),
)
// Interface for a processing pass.
type Interface interface {
Execute(*ir.File) error
}
// Func adapts a function to the pass Interface.
type Func func(*ir.File) error
// Execute calls p.
func (p Func) Execute(f *ir.File) error {
return p(f)
}
// FunctionPass is a convenience for implementing a full file pass with a
// function that operates on each avo Function independently.
type FunctionPass func(*ir.Function) error
// Execute calls p on every function in the file. Exits on the first error.
func (p FunctionPass) Execute(f *ir.File) error {
for _, fn := range f.Functions() {
if err := p(fn); err != nil {
return err
}
}
return nil
}
// InstructionPass is a convenience for implementing a full file pass with a
// function that operates on each Instruction independently.
type InstructionPass func(*ir.Instruction) error
// Execute calls p on every instruction in the file. Exits on the first error.
func (p InstructionPass) Execute(f *ir.File) error {
for _, fn := range f.Functions() {
for _, i := range fn.Instructions() {
if err := p(i); err != nil {
return err
}
}
}
return nil
}
// Concat returns a pass that executes the given passes in order, stopping on the first error.
func Concat(passes ...Interface) Interface {
return Func(func(f *ir.File) error {
for _, p := range passes {
if err := p.Execute(f); err != nil {
return err
}
}
return nil
})
}
// Output pass prints a file.
type Output struct {
Writer io.WriteCloser
Printer printer.Printer
}
// Execute prints f with the configured Printer and writes output to Writer.
func (o *Output) Execute(f *ir.File) error {
b, err := o.Printer.Print(f)
if err != nil {
return err
}
if _, err = o.Writer.Write(b); err != nil {
return err
}
return o.Writer.Close()
}
-139
View File
@@ -1,139 +0,0 @@
package pass
import (
"errors"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
"github.com/mmcloughlin/avo/reg"
)
// ZeroExtend32BitOutputs applies the rule that "32-bit operands generate a
// 32-bit result, zero-extended to a 64-bit result in the destination
// general-purpose register" (Intel Software Developers Manual, Volume 1,
// 3.4.1.1).
func ZeroExtend32BitOutputs(i *ir.Instruction) error {
for j, op := range i.Outputs {
if !operand.IsR32(op) {
continue
}
r, ok := op.(reg.GP)
if !ok {
panic("r32 operand should satisfy reg.GP")
}
i.Outputs[j] = r.As64()
}
return nil
}
// Liveness computes register liveness.
func Liveness(fn *ir.Function) error {
// Note this implementation is initially naive so as to be "obviously correct".
// There are a well-known optimizations we can apply if necessary.
is := fn.Instructions()
// Process instructions in reverse: poor approximation to topological sort.
// TODO(mbm): process instructions in topological sort order
for l, r := 0, len(is)-1; l < r; l, r = l+1, r-1 {
is[l], is[r] = is[r], is[l]
}
// Initialize.
for _, i := range is {
i.LiveIn = reg.NewMaskSetFromRegisters(i.InputRegisters())
i.LiveOut = reg.NewEmptyMaskSet()
}
// Iterative dataflow analysis.
for {
changes := false
for _, i := range is {
// out[n] = UNION[s IN succ[n]] in[s]
for _, s := range i.Succ {
if s == nil {
continue
}
changes = i.LiveOut.Update(s.LiveIn) || changes
}
// in[n] = use[n] UNION (out[n] - def[n])
def := reg.NewMaskSetFromRegisters(i.OutputRegisters())
changes = i.LiveIn.Update(i.LiveOut.Difference(def)) || changes
}
if !changes {
break
}
}
return nil
}
// AllocateRegisters performs register allocation.
func AllocateRegisters(fn *ir.Function) error {
// Populate allocators (one per kind).
as := map[reg.Kind]*Allocator{}
for _, i := range fn.Instructions() {
for _, r := range i.Registers() {
k := r.Kind()
if _, found := as[k]; !found {
a, err := NewAllocatorForKind(k)
if err != nil {
return err
}
as[k] = a
}
as[k].Add(r.ID())
}
}
// Record register interferences.
for _, i := range fn.Instructions() {
for _, d := range i.OutputRegisters() {
k := d.Kind()
out := i.LiveOut.OfKind(k)
out.DiscardRegister(d)
as[k].AddInterferenceSet(d, out)
}
}
// Execute register allocation.
fn.Allocation = reg.NewEmptyAllocation()
for _, a := range as {
al, err := a.Allocate()
if err != nil {
return err
}
if err := fn.Allocation.Merge(al); err != nil {
return err
}
}
return nil
}
// BindRegisters applies the result of register allocation, replacing all virtual registers with their assigned physical registers.
func BindRegisters(fn *ir.Function) error {
for _, i := range fn.Instructions() {
for idx := range i.Operands {
i.Operands[idx] = operand.ApplyAllocation(i.Operands[idx], fn.Allocation)
}
}
return nil
}
// VerifyAllocation performs sanity checks following register allocation.
func VerifyAllocation(fn *ir.Function) error {
// All registers should be physical.
for _, i := range fn.Instructions() {
for _, r := range i.Registers() {
if reg.ToPhysical(r) == nil {
return errors.New("non physical register found")
}
}
}
return nil
}
-42
View File
@@ -1,42 +0,0 @@
package pass
import (
"github.com/mmcloughlin/avo/attr"
"github.com/mmcloughlin/avo/ir"
)
// IncludeTextFlagHeader includes textflag.h if necessary.
func IncludeTextFlagHeader(f *ir.File) error {
const textflagheader = "textflag.h"
// Check if we already have it.
for _, path := range f.Includes {
if path == textflagheader {
return nil
}
}
// Add it if necessary.
if requirestextflags(f) {
f.Includes = append(f.Includes, textflagheader)
}
return nil
}
// requirestextflags returns whether the file uses flags in the textflags.h header.
func requirestextflags(f *ir.File) bool {
for _, s := range f.Sections {
var a attr.Attribute
switch s := s.(type) {
case *ir.Function:
a = s.Attributes
case *ir.Global:
a = s.Attributes
}
if a.ContainsTextFlags() {
return true
}
}
return false
}
-32
View File
@@ -1,32 +0,0 @@
package pass
import (
"errors"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
)
// Verify pass validates an avo file.
var Verify = Concat(
InstructionPass(VerifyMemOperands),
)
// VerifyMemOperands checks the instruction's memory operands.
func VerifyMemOperands(i *ir.Instruction) error {
for _, op := range i.Operands {
m, ok := op.(operand.Mem)
if !ok {
continue
}
if m.Base == nil {
return errors.New("bad memory operand: missing base register")
}
if m.Index != nil && m.Scale == 0 {
return errors.New("bad memory operand: index register with scale 0")
}
}
return nil
}
-186
View File
@@ -1,186 +0,0 @@
package printer
import (
"strconv"
"strings"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/ir"
"github.com/mmcloughlin/avo/operand"
)
// dot is the pesky unicode dot used in Go assembly.
const dot = "\u00b7"
type goasm struct {
cfg Config
prnt.Generator
instructions []*ir.Instruction
clear bool
}
// NewGoAsm constructs a printer for writing Go assembly files.
func NewGoAsm(cfg Config) Printer {
return &goasm{cfg: cfg}
}
func (p *goasm) Print(f *ir.File) ([]byte, error) {
p.header(f)
for _, s := range f.Sections {
switch s := s.(type) {
case *ir.Function:
p.function(s)
case *ir.Global:
p.global(s)
default:
panic("unknown section type")
}
}
return p.Result()
}
func (p *goasm) header(f *ir.File) {
p.Comment(p.cfg.GeneratedWarning())
if len(f.Constraints) > 0 {
p.NL()
p.Printf(f.Constraints.GoString())
}
if len(f.Includes) > 0 {
p.NL()
p.includes(f.Includes)
}
}
func (p *goasm) includes(paths []string) {
for _, path := range paths {
p.Printf("#include \"%s\"\n", path)
}
}
func (p *goasm) function(f *ir.Function) {
p.NL()
p.Comment(f.Stub())
if len(f.ISA) > 0 {
p.Comment("Requires: " + strings.Join(f.ISA, ", "))
}
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L166-L176
//
// if p.As == ATEXT {
// // If there are attributes, print them. Otherwise, skip the comma.
// // In short, print one of these two:
// // TEXT foo(SB), DUPOK|NOSPLIT, $0
// // TEXT foo(SB), $0
// s := p.From.Sym.Attribute.TextAttrString()
// if s != "" {
// fmt.Fprintf(&buf, "%s%s", sep, s)
// sep = ", "
// }
// }
//
p.Printf("TEXT %s%s(SB)", dot, f.Name)
if f.Attributes != 0 {
p.Printf(", %s", f.Attributes.Asm())
}
p.Printf(", %s\n", textsize(f))
p.clear = true
for _, node := range f.Nodes {
switch n := node.(type) {
case *ir.Instruction:
p.instruction(n)
if n.IsTerminal || n.IsUnconditionalBranch() {
p.flush()
}
case ir.Label:
p.flush()
p.ensureclear()
p.Printf("%s:\n", n)
case *ir.Comment:
p.flush()
p.ensureclear()
for _, line := range n.Lines {
p.Printf("\t// %s\n", line)
}
default:
panic("unexpected node type")
}
}
p.flush()
}
func (p *goasm) instruction(i *ir.Instruction) {
p.instructions = append(p.instructions, i)
p.clear = false
}
func (p *goasm) flush() {
if len(p.instructions) == 0 {
return
}
// Determine instruction width. Instructions with no operands are not
// considered in this calculation.
width := 0
for _, i := range p.instructions {
if len(i.Operands) > 0 && len(i.Opcode) > width {
width = len(i.Opcode)
}
}
// Output instruction block.
for _, i := range p.instructions {
if len(i.Operands) > 0 {
p.Printf("\t%-*s%s\n", width+1, i.Opcode, joinOperands(i.Operands))
} else {
p.Printf("\t%s\n", i.Opcode)
}
}
p.instructions = nil
}
func (p *goasm) ensureclear() {
if !p.clear {
p.NL()
p.clear = true
}
}
func (p *goasm) global(g *ir.Global) {
p.NL()
for _, d := range g.Data {
a := operand.NewDataAddr(g.Symbol, d.Offset)
p.Printf("DATA %s/%d, %s\n", a.Asm(), d.Value.Bytes(), d.Value.Asm())
}
p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size)
}
func textsize(f *ir.Function) string {
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265
//
// case TYPE_TEXTSIZE:
// if a.Val.(int32) == objabi.ArgsSizeUnknown {
// str = fmt.Sprintf("$%d", a.Offset)
// } else {
// str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
// }
//
s := "$" + strconv.Itoa(f.FrameBytes())
if argsize := f.ArgumentBytes(); argsize > 0 {
return s + "-" + strconv.Itoa(argsize)
}
return s
}
func joinOperands(operands []operand.Op) string {
asm := make([]string, len(operands))
for i, op := range operands {
asm[i] = op.Asm()
}
return strings.Join(asm, ", ")
}
-98
View File
@@ -1,98 +0,0 @@
// Package printer implements printing of avo files in various formats.
package printer
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mmcloughlin/avo/internal/stack"
"github.com/mmcloughlin/avo/ir"
)
// Printer can produce output for an avo File.
type Printer interface {
Print(*ir.File) ([]byte, error)
}
// Builder can construct a printer.
type Builder func(Config) Printer
// Config represents general printing configuration.
type Config struct {
// Command-line arguments passed to the generator. If provided, this will be
// included in a code generation warning.
Argv []string
// Name of the code generator.
Name string
// Name of Go package the generated code will belong to.
Pkg string
}
// NewDefaultConfig produces a config with Name "avo".
// The package name is guessed from the current directory.
func NewDefaultConfig() Config {
return Config{
Name: "avo",
Pkg: pkg(),
}
}
// NewArgvConfig constructs a Config from os.Args.
// The package name is guessed from the current directory.
func NewArgvConfig() Config {
return Config{
Argv: os.Args,
Pkg: pkg(),
}
}
// NewGoRunConfig produces a Config for a generator that's expected to be
// executed via "go run ...".
func NewGoRunConfig() Config {
path := mainfile()
if path == "" {
return NewDefaultConfig()
}
argv := []string{"go", "run", filepath.Base(path)}
if len(os.Args) > 1 {
argv = append(argv, os.Args[1:]...)
}
return Config{
Argv: argv,
Pkg: pkg(),
}
}
// GeneratedBy returns a description of the code generator.
func (c Config) GeneratedBy() string {
if c.Argv == nil {
return c.Name
}
return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
}
// GeneratedWarning returns text for a code generation warning. Conforms to https://golang.org/s/generatedcode.
func (c Config) GeneratedWarning() string {
return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
}
// mainfile attempts to determine the file path of the main function by
// inspecting the stack. Returns empty string on failure.
func mainfile() string {
if m := stack.Main(); m != nil {
return m.File
}
return ""
}
// pkg guesses the name of the package from the working directory.
func pkg() string {
if cwd, err := os.Getwd(); err == nil {
return filepath.Base(cwd)
}
return ""
}
-45
View File
@@ -1,45 +0,0 @@
package printer
import (
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/ir"
)
type stubs struct {
cfg Config
prnt.Generator
}
// NewStubs constructs a printer for writing stub function declarations.
func NewStubs(cfg Config) Printer {
return &stubs{cfg: cfg}
}
func (s *stubs) Print(f *ir.File) ([]byte, error) {
s.Comment(s.cfg.GeneratedWarning())
if len(f.Constraints) > 0 {
s.NL()
s.Printf(f.Constraints.GoString())
}
s.NL()
s.Printf("package %s\n", s.cfg.Pkg)
for _, fn := range f.Functions() {
s.NL()
s.Comment(fn.Doc...)
for _, pragma := range fn.Pragmas {
s.pragma(pragma)
}
s.Printf("%s\n", fn.Stub())
}
return s.Result()
}
func (s *stubs) pragma(p ir.Pragma) {
s.Printf("//go:%s", p.Directive)
for _, arg := range p.Arguments {
s.Printf(" %s", arg)
}
s.NL()
}
-54
View File
@@ -1,54 +0,0 @@
package reg
// Collection represents a collection of virtual registers. This is primarily
// useful for allocating virtual registers with distinct IDs.
type Collection struct {
idx map[Kind]Index
}
// NewCollection builds an empty register collection.
func NewCollection() *Collection {
return &Collection{
idx: map[Kind]Index{},
}
}
// VirtualRegister allocates and returns a new virtual register of the given kind and width.
func (c *Collection) VirtualRegister(k Kind, s Spec) Virtual {
idx := c.idx[k]
c.idx[k]++
return NewVirtual(idx, k, s)
}
// GP8L allocates and returns a general-purpose 8-bit register (low byte).
func (c *Collection) GP8L() GPVirtual { return c.GP(S8L) }
// GP8H allocates and returns a general-purpose 8-bit register (high byte).
func (c *Collection) GP8H() GPVirtual { return c.GP(S8H) }
// GP8 allocates and returns a general-purpose 8-bit register (low byte).
func (c *Collection) GP8() GPVirtual { return c.GP8L() }
// GP16 allocates and returns a general-purpose 16-bit register.
func (c *Collection) GP16() GPVirtual { return c.GP(S16) }
// GP32 allocates and returns a general-purpose 32-bit register.
func (c *Collection) GP32() GPVirtual { return c.GP(S32) }
// GP64 allocates and returns a general-purpose 64-bit register.
func (c *Collection) GP64() GPVirtual { return c.GP(S64) }
// GP allocates and returns a general-purpose register of the given width.
func (c *Collection) GP(s Spec) GPVirtual { return newgpv(c.VirtualRegister(KindGP, s)) }
// XMM allocates and returns a 128-bit vector register.
func (c *Collection) XMM() VecVirtual { return c.Vec(S128) }
// YMM allocates and returns a 256-bit vector register.
func (c *Collection) YMM() VecVirtual { return c.Vec(S256) }
// ZMM allocates and returns a 512-bit vector register.
func (c *Collection) ZMM() VecVirtual { return c.Vec(S512) }
// Vec allocates and returns a vector register of the given width.
func (c *Collection) Vec(s Spec) VecVirtual { return newvecv(c.VirtualRegister(KindVector, s)) }
-2
View File
@@ -1,2 +0,0 @@
// Package reg provides types for physical and virtual registers, and definitions of x86-64 register families.
package reg
-112
View File
@@ -1,112 +0,0 @@
package reg
// MaskSet maps register IDs to masks.
type MaskSet map[ID]uint16
// NewEmptyMaskSet builds an empty register mask set.
func NewEmptyMaskSet() MaskSet {
return MaskSet{}
}
// NewMaskSetFromRegisters forms a mask set from the given register list.
func NewMaskSetFromRegisters(rs []Register) MaskSet {
s := NewEmptyMaskSet()
for _, r := range rs {
s.AddRegister(r)
}
return s
}
// Clone returns a copy of s.
func (s MaskSet) Clone() MaskSet {
c := NewEmptyMaskSet()
for id, mask := range s {
c.Add(id, mask)
}
return c
}
// Add mask to the given register ID.
// Reports whether this made any change to the set.
func (s MaskSet) Add(id ID, mask uint16) bool {
if (s[id] & mask) == mask {
return false
}
s[id] |= mask
return true
}
// AddRegister is a convenience for adding the register's (ID, mask) to the set.
// Reports whether this made any change to the set.
func (s MaskSet) AddRegister(r Register) bool {
return s.Add(r.ID(), r.Mask())
}
// Discard clears masked bits from register ID.
// Reports whether this made any change to the set.
func (s MaskSet) Discard(id ID, mask uint16) bool {
if curr, found := s[id]; !found || (curr&mask) == 0 {
return false
}
s[id] &^= mask
if s[id] == 0 {
delete(s, id)
}
return true
}
// DiscardRegister is a convenience for discarding the register's (ID, mask) from the set.
// Reports whether this made any change to the set.
func (s MaskSet) DiscardRegister(r Register) bool {
return s.Discard(r.ID(), r.Mask())
}
// Update adds masks in t to s.
// Reports whether this made any change to the set.
func (s MaskSet) Update(t MaskSet) bool {
change := false
for id, mask := range t {
change = s.Add(id, mask) || change
}
return change
}
// Difference returns the set of registers in s but not t.
func (s MaskSet) Difference(t MaskSet) MaskSet {
d := s.Clone()
d.DifferenceUpdate(t)
return d
}
// DifferenceUpdate removes every element of t from s.
func (s MaskSet) DifferenceUpdate(t MaskSet) bool {
change := false
for id, mask := range t {
change = s.Discard(id, mask) || change
}
return change
}
// Equals returns true if s and t contain the same masks.
func (s MaskSet) Equals(t MaskSet) bool {
if len(s) != len(t) {
return false
}
for id, mask := range s {
if _, found := t[id]; !found || mask != t[id] {
return false
}
}
return true
}
// OfKind returns the set of elements of s with kind k.
func (s MaskSet) OfKind(k Kind) MaskSet {
t := NewEmptyMaskSet()
for id, mask := range s {
if id.Kind() == k {
t.Add(id, mask)
}
}
return t
}
-304
View File
@@ -1,304 +0,0 @@
package reg
import (
"errors"
"fmt"
)
// Kind is a class of registers.
type Kind uint8
// Index of a register within a kind.
type Index uint16
// Family is a collection of Physical registers of a common kind.
type Family struct {
Kind Kind
registers []Physical
}
// define builds a register and adds it to the Family.
func (f *Family) define(s Spec, idx Index, name string, flags ...Info) Physical {
r := newregister(f, s, idx, name, flags...)
f.add(r)
return r
}
// add r to the family.
func (f *Family) add(r Physical) {
if r.Kind() != f.Kind {
panic("bad kind")
}
f.registers = append(f.registers, r)
}
// Virtual returns a virtual register from this family's kind.
func (f *Family) Virtual(idx Index, s Spec) Virtual {
return NewVirtual(idx, f.Kind, s)
}
// Registers returns the registers in this family.
func (f *Family) Registers() []Physical {
return append([]Physical(nil), f.registers...)
}
// Lookup returns the register with given physical index and spec. Returns nil if no such register exists.
func (f *Family) Lookup(idx Index, s Spec) Physical {
for _, r := range f.registers {
if r.PhysicalIndex() == idx && r.Mask() == s.Mask() {
return r
}
}
return nil
}
// ID is a register identifier.
type ID uint32
// newid builds a new register ID from the virtual flag v, kind and index.
func newid(v uint8, kind Kind, idx Index) ID {
return ID(v) | (ID(kind) << 8) | (ID(idx) << 16)
}
// IsVirtual reports whether this is an ID for a virtual register.
func (id ID) IsVirtual() bool { return (id & 1) == 1 }
// IsPhysical reports whether this is an ID for a physical register.
func (id ID) IsPhysical() bool { return !id.IsVirtual() }
// Kind extracts the kind from the register ID.
func (id ID) Kind() Kind { return Kind(id >> 8) }
// Index extracts the index from the register ID.
func (id ID) Index() Index { return Index(id >> 16) }
// Register represents a virtual or physical register.
type Register interface {
ID() ID
Kind() Kind
Size() uint
Mask() uint16
Asm() string
as(Spec) Register
spec() Spec
register()
}
// Equal reports whether a and b are equal registers.
func Equal(a, b Register) bool {
return (a.ID() == b.ID()) && (a.Mask() == b.Mask())
}
// Virtual is a register of a given type and size, not yet allocated to a physical register.
type Virtual interface {
VirtualIndex() Index
Register
}
// ToVirtual converts r to Virtual if possible, otherwise returns nil.
func ToVirtual(r Register) Virtual {
if v, ok := r.(Virtual); ok {
return v
}
return nil
}
type virtual struct {
idx Index
kind Kind
Spec
}
// NewVirtual builds a Virtual register.
func NewVirtual(idx Index, k Kind, s Spec) Virtual {
return virtual{
idx: idx,
kind: k,
Spec: s,
}
}
func (v virtual) ID() ID { return newid(1, v.kind, v.idx) }
func (v virtual) VirtualIndex() Index { return v.idx }
func (v virtual) Kind() Kind { return v.kind }
func (v virtual) Asm() string {
// TODO(mbm): decide on virtual register syntax
return fmt.Sprintf("<virtual:%v:%v:%v>", v.idx, v.Kind(), v.Size())
}
func (v virtual) as(s Spec) Register {
return virtual{
idx: v.idx,
kind: v.kind,
Spec: s,
}
}
func (v virtual) spec() Spec { return v.Spec }
func (v virtual) register() {}
// Info is a bitmask of register properties.
type Info uint8
// Defined register Info flags.
const (
None Info = 0
Restricted Info = 1 << iota
)
// Physical is a concrete register.
type Physical interface {
PhysicalIndex() Index
Info() Info
Register
}
// ToPhysical converts r to Physical if possible, otherwise returns nil.
func ToPhysical(r Register) Physical {
if p, ok := r.(Physical); ok {
return p
}
return nil
}
// register implements Physical.
type register struct {
family *Family
idx Index
name string
info Info
Spec
}
func newregister(f *Family, s Spec, idx Index, name string, flags ...Info) register {
r := register{
family: f,
idx: idx,
name: name,
info: None,
Spec: s,
}
for _, flag := range flags {
r.info |= flag
}
return r
}
func (r register) ID() ID { return newid(0, r.Kind(), r.idx) }
func (r register) PhysicalIndex() Index { return r.idx }
func (r register) Kind() Kind { return r.family.Kind }
func (r register) Asm() string { return r.name }
func (r register) Info() Info { return r.info }
func (r register) as(s Spec) Register {
return r.family.Lookup(r.PhysicalIndex(), s)
}
func (r register) spec() Spec { return r.Spec }
func (r register) register() {}
// Spec defines the size of a register as well as the bit ranges it occupies in
// an underlying physical register.
type Spec uint16
// Spec values required for x86-64.
const (
S0 Spec = 0x0 // zero value reserved for pseudo registers
S8L Spec = 0x1
S8H Spec = 0x2
S8 = S8L
S16 Spec = 0x3
S32 Spec = 0x7
S64 Spec = 0xf
S128 Spec = 0x1f
S256 Spec = 0x3f
S512 Spec = 0x7f
)
// Mask returns a mask representing which bytes of an underlying register are
// used by this register. This is almost always the low bytes, except for the
// case of the high-byte registers. If bit n of the mask is set, this means
// bytes 2^(n-1) to 2^n-1 are used.
func (s Spec) Mask() uint16 {
return uint16(s)
}
// Size returns the register width in bytes.
func (s Spec) Size() uint {
x := uint(s)
return (x >> 1) + (x & 1)
}
// LookupPhysical returns the physical register with the given parameters, or nil if not found.
func LookupPhysical(k Kind, idx Index, s Spec) Physical {
f := FamilyOfKind(k)
if f == nil {
return nil
}
return f.Lookup(idx, s)
}
// LookupID returns the physical register with the given id and spec, or nil if not found.
func LookupID(id ID, s Spec) Physical {
if id.IsVirtual() {
return nil
}
return LookupPhysical(id.Kind(), id.Index(), s)
}
// Allocation records a register allocation.
type Allocation map[ID]ID
// NewEmptyAllocation builds an empty register allocation.
func NewEmptyAllocation() Allocation {
return Allocation{}
}
// Merge allocations from b into a. Errors if there is disagreement on a common
// register.
func (a Allocation) Merge(b Allocation) error {
for id, p := range b {
if alt, found := a[id]; found && alt != p {
return errors.New("disagreement on overlapping register")
}
a[id] = p
}
return nil
}
// LookupDefault returns the register ID assigned by this allocation, returning
// id if none is found.
func (a Allocation) LookupDefault(id ID) ID {
if _, found := a[id]; found {
return a[id]
}
return id
}
// LookupRegister the allocation for register r, or return nil if there is none.
func (a Allocation) LookupRegister(r Register) Physical {
// Return immediately if it is already a physical register.
if p := ToPhysical(r); p != nil {
return p
}
// Lookup an allocation for this virtual ID.
id, found := a[r.ID()]
if !found {
return nil
}
return LookupID(id, r.spec())
}
// LookupRegisterDefault returns the register assigned to r, or r itself if there is none.
func (a Allocation) LookupRegisterDefault(r Register) Register {
if r == nil {
return nil
}
if p := a.LookupRegister(r); p != nil {
return p
}
return r
}
-331
View File
@@ -1,331 +0,0 @@
package reg
// Register kinds.
const (
KindPseudo Kind = iota
KindGP
KindVector
)
// Declare register families.
var (
Pseudo = &Family{Kind: KindPseudo}
GeneralPurpose = &Family{Kind: KindGP}
Vector = &Family{Kind: KindVector}
Families = []*Family{
Pseudo,
GeneralPurpose,
Vector,
}
)
var familiesByKind = map[Kind]*Family{}
func init() {
for _, f := range Families {
familiesByKind[f.Kind] = f
}
}
// FamilyOfKind returns the Family of registers of the given kind, or nil if not found.
func FamilyOfKind(k Kind) *Family {
return familiesByKind[k]
}
// Pseudo registers.
var (
FramePointer = Pseudo.define(S0, 0, "FP")
ProgramCounter = Pseudo.define(S0, 0, "PC")
StaticBase = Pseudo.define(S0, 0, "SB")
StackPointer = Pseudo.define(S0, 0, "SP")
)
// GP provides additional methods for general purpose registers.
type GP interface {
As8() Register
As8L() Register
As8H() Register
As16() Register
As32() Register
As64() Register
}
// GPPhysical is a general-purpose physical register.
type GPPhysical interface {
Physical
GP
}
type gpp struct {
Physical
}
func newgpp(r Physical) GPPhysical { return gpp{Physical: r} }
func (p gpp) As8() Register { return newgpp(p.as(S8).(Physical)) }
func (p gpp) As8L() Register { return newgpp(p.as(S8L).(Physical)) }
func (p gpp) As8H() Register { return newgpp(p.as(S8H).(Physical)) }
func (p gpp) As16() Register { return newgpp(p.as(S16).(Physical)) }
func (p gpp) As32() Register { return newgpp(p.as(S32).(Physical)) }
func (p gpp) As64() Register { return newgpp(p.as(S64).(Physical)) }
// GPVirtual is a general-purpose virtual register.
type GPVirtual interface {
Virtual
GP
}
type gpv struct {
Virtual
}
func newgpv(v Virtual) GPVirtual { return gpv{Virtual: v} }
func (v gpv) As8() Register { return newgpv(v.as(S8).(Virtual)) }
func (v gpv) As8L() Register { return newgpv(v.as(S8L).(Virtual)) }
func (v gpv) As8H() Register { return newgpv(v.as(S8H).(Virtual)) }
func (v gpv) As16() Register { return newgpv(v.as(S16).(Virtual)) }
func (v gpv) As32() Register { return newgpv(v.as(S32).(Virtual)) }
func (v gpv) As64() Register { return newgpv(v.as(S64).(Virtual)) }
func gp(s Spec, id Index, name string, flags ...Info) GPPhysical {
r := newgpp(newregister(GeneralPurpose, s, id, name, flags...))
GeneralPurpose.add(r)
return r
}
// General purpose registers.
var (
// Low byte
AL = gp(S8L, 0, "AL")
CL = gp(S8L, 1, "CL")
DL = gp(S8L, 2, "DL")
BL = gp(S8L, 3, "BL")
// High byte
AH = gp(S8H, 0, "AH")
CH = gp(S8H, 1, "CH")
DH = gp(S8H, 2, "DH")
BH = gp(S8H, 3, "BH")
// 8-bit
SPB = gp(S8, 4, "SP", Restricted)
BPB = gp(S8, 5, "BP")
SIB = gp(S8, 6, "SI")
DIB = gp(S8, 7, "DI")
R8B = gp(S8, 8, "R8")
R9B = gp(S8, 9, "R9")
R10B = gp(S8, 10, "R10")
R11B = gp(S8, 11, "R11")
R12B = gp(S8, 12, "R12")
R13B = gp(S8, 13, "R13")
R14B = gp(S8, 14, "R14")
R15B = gp(S8, 15, "R15")
// 16-bit
AX = gp(S16, 0, "AX")
CX = gp(S16, 1, "CX")
DX = gp(S16, 2, "DX")
BX = gp(S16, 3, "BX")
SP = gp(S16, 4, "SP", Restricted)
BP = gp(S16, 5, "BP")
SI = gp(S16, 6, "SI")
DI = gp(S16, 7, "DI")
R8W = gp(S16, 8, "R8")
R9W = gp(S16, 9, "R9")
R10W = gp(S16, 10, "R10")
R11W = gp(S16, 11, "R11")
R12W = gp(S16, 12, "R12")
R13W = gp(S16, 13, "R13")
R14W = gp(S16, 14, "R14")
R15W = gp(S16, 15, "R15")
// 32-bit
EAX = gp(S32, 0, "AX")
ECX = gp(S32, 1, "CX")
EDX = gp(S32, 2, "DX")
EBX = gp(S32, 3, "BX")
ESP = gp(S32, 4, "SP", Restricted)
EBP = gp(S32, 5, "BP")
ESI = gp(S32, 6, "SI")
EDI = gp(S32, 7, "DI")
R8L = gp(S32, 8, "R8")
R9L = gp(S32, 9, "R9")
R10L = gp(S32, 10, "R10")
R11L = gp(S32, 11, "R11")
R12L = gp(S32, 12, "R12")
R13L = gp(S32, 13, "R13")
R14L = gp(S32, 14, "R14")
R15L = gp(S32, 15, "R15")
// 64-bit
RAX = gp(S64, 0, "AX")
RCX = gp(S64, 1, "CX")
RDX = gp(S64, 2, "DX")
RBX = gp(S64, 3, "BX")
RSP = gp(S64, 4, "SP", Restricted)
RBP = gp(S64, 5, "BP")
RSI = gp(S64, 6, "SI")
RDI = gp(S64, 7, "DI")
R8 = gp(S64, 8, "R8")
R9 = gp(S64, 9, "R9")
R10 = gp(S64, 10, "R10")
R11 = gp(S64, 11, "R11")
R12 = gp(S64, 12, "R12")
R13 = gp(S64, 13, "R13")
R14 = gp(S64, 14, "R14")
R15 = gp(S64, 15, "R15")
)
// Vec provides methods for vector registers.
type Vec interface {
AsX() Register
AsY() Register
AsZ() Register
}
// VecPhysical is a physical vector register.
type VecPhysical interface {
Physical
Vec
}
type vecp struct {
Physical
Vec
}
func newvecp(r Physical) VecPhysical { return vecp{Physical: r} }
func (p vecp) AsX() Register { return newvecp(p.as(S128).(Physical)) }
func (p vecp) AsY() Register { return newvecp(p.as(S256).(Physical)) }
func (p vecp) AsZ() Register { return newvecp(p.as(S512).(Physical)) }
// VecVirtual is a virtual vector register.
type VecVirtual interface {
Virtual
Vec
}
type vecv struct {
Virtual
Vec
}
func newvecv(v Virtual) VecVirtual { return vecv{Virtual: v} }
func (v vecv) AsX() Register { return newvecv(v.as(S128).(Virtual)) }
func (v vecv) AsY() Register { return newvecv(v.as(S256).(Virtual)) }
func (v vecv) AsZ() Register { return newvecv(v.as(S512).(Virtual)) }
func vec(s Spec, id Index, name string, flags ...Info) VecPhysical {
r := newvecp(newregister(Vector, s, id, name, flags...))
Vector.add(r)
return r
}
// Vector registers.
var (
// 128-bit
X0 = vec(S128, 0, "X0")
X1 = vec(S128, 1, "X1")
X2 = vec(S128, 2, "X2")
X3 = vec(S128, 3, "X3")
X4 = vec(S128, 4, "X4")
X5 = vec(S128, 5, "X5")
X6 = vec(S128, 6, "X6")
X7 = vec(S128, 7, "X7")
X8 = vec(S128, 8, "X8")
X9 = vec(S128, 9, "X9")
X10 = vec(S128, 10, "X10")
X11 = vec(S128, 11, "X11")
X12 = vec(S128, 12, "X12")
X13 = vec(S128, 13, "X13")
X14 = vec(S128, 14, "X14")
X15 = vec(S128, 15, "X15")
X16 = vec(S128, 16, "X16")
X17 = vec(S128, 17, "X17")
X18 = vec(S128, 18, "X18")
X19 = vec(S128, 19, "X19")
X20 = vec(S128, 20, "X20")
X21 = vec(S128, 21, "X21")
X22 = vec(S128, 22, "X22")
X23 = vec(S128, 23, "X23")
X24 = vec(S128, 24, "X24")
X25 = vec(S128, 25, "X25")
X26 = vec(S128, 26, "X26")
X27 = vec(S128, 27, "X27")
X28 = vec(S128, 28, "X28")
X29 = vec(S128, 29, "X29")
X30 = vec(S128, 30, "X30")
X31 = vec(S128, 31, "X31")
// 256-bit
Y0 = vec(S256, 0, "Y0")
Y1 = vec(S256, 1, "Y1")
Y2 = vec(S256, 2, "Y2")
Y3 = vec(S256, 3, "Y3")
Y4 = vec(S256, 4, "Y4")
Y5 = vec(S256, 5, "Y5")
Y6 = vec(S256, 6, "Y6")
Y7 = vec(S256, 7, "Y7")
Y8 = vec(S256, 8, "Y8")
Y9 = vec(S256, 9, "Y9")
Y10 = vec(S256, 10, "Y10")
Y11 = vec(S256, 11, "Y11")
Y12 = vec(S256, 12, "Y12")
Y13 = vec(S256, 13, "Y13")
Y14 = vec(S256, 14, "Y14")
Y15 = vec(S256, 15, "Y15")
Y16 = vec(S256, 16, "Y16")
Y17 = vec(S256, 17, "Y17")
Y18 = vec(S256, 18, "Y18")
Y19 = vec(S256, 19, "Y19")
Y20 = vec(S256, 20, "Y20")
Y21 = vec(S256, 21, "Y21")
Y22 = vec(S256, 22, "Y22")
Y23 = vec(S256, 23, "Y23")
Y24 = vec(S256, 24, "Y24")
Y25 = vec(S256, 25, "Y25")
Y26 = vec(S256, 26, "Y26")
Y27 = vec(S256, 27, "Y27")
Y28 = vec(S256, 28, "Y28")
Y29 = vec(S256, 29, "Y29")
Y30 = vec(S256, 30, "Y30")
Y31 = vec(S256, 31, "Y31")
// 512-bit
Z0 = vec(S512, 0, "Z0")
Z1 = vec(S512, 1, "Z1")
Z2 = vec(S512, 2, "Z2")
Z3 = vec(S512, 3, "Z3")
Z4 = vec(S512, 4, "Z4")
Z5 = vec(S512, 5, "Z5")
Z6 = vec(S512, 6, "Z6")
Z7 = vec(S512, 7, "Z7")
Z8 = vec(S512, 8, "Z8")
Z9 = vec(S512, 9, "Z9")
Z10 = vec(S512, 10, "Z10")
Z11 = vec(S512, 11, "Z11")
Z12 = vec(S512, 12, "Z12")
Z13 = vec(S512, 13, "Z13")
Z14 = vec(S512, 14, "Z14")
Z15 = vec(S512, 15, "Z15")
Z16 = vec(S512, 16, "Z16")
Z17 = vec(S512, 17, "Z17")
Z18 = vec(S512, 18, "Z18")
Z19 = vec(S512, 19, "Z19")
Z20 = vec(S512, 20, "Z20")
Z21 = vec(S512, 21, "Z21")
Z22 = vec(S512, 22, "Z22")
Z23 = vec(S512, 23, "Z23")
Z24 = vec(S512, 24, "Z24")
Z25 = vec(S512, 25, "Z25")
Z26 = vec(S512, 26, "Z26")
Z27 = vec(S512, 27, "Z27")
Z28 = vec(S512, 28, "Z28")
Z29 = vec(S512, 29, "Z29")
Z30 = vec(S512, 30, "Z30")
Z31 = vec(S512, 31, "Z31")
)
-62
View File
@@ -1,62 +0,0 @@
// Package src provides types for working with source files.
package src
import (
"os"
"path/filepath"
"runtime"
"strconv"
)
// Position represents a position in a source file.
type Position struct {
Filename string
Line int // 1-up
}
// FramePosition returns the Position of the given stack frame.
func FramePosition(f runtime.Frame) Position {
return Position{
Filename: f.File,
Line: f.Line,
}
}
// IsValid reports whether the position is valid: Line must be positive, but
// Filename may be empty.
func (p Position) IsValid() bool {
return p.Line > 0
}
// String represents Position as a string.
func (p Position) String() string {
if !p.IsValid() {
return "-"
}
var s string
if p.Filename != "" {
s += p.Filename + ":"
}
s += strconv.Itoa(p.Line)
return s
}
// Rel returns Position relative to basepath. If the given filename cannot be
// expressed relative to basepath the position will be returned unchanged.
func (p Position) Rel(basepath string) Position {
q := p
if rel, err := filepath.Rel(basepath, q.Filename); err == nil {
q.Filename = rel
}
return q
}
// Relwd returns Position relative to the current working directory. Returns p
// unchanged if the working directory cannot be determined, or the filename
// cannot be expressed relative to the working directory.
func (p Position) Relwd() Position {
if wd, err := os.Getwd(); err == nil {
return p.Rel(wd)
}
return p
}
-2
View File
@@ -1,2 +0,0 @@
// Package x86 provides constructors for all x86-64 instructions.
package x86
-4
View File
@@ -1,4 +0,0 @@
package x86
//go:generate avogen -output zctors.go ctors
//go:generate avogen -output zctors_test.go ctorstest
-34629
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More