Compare commits

..
1 Commits
Author SHA1 Message Date
xtaci b7167b5859 upd deps 2022-06-27 20:12:18 +08:00
297 changed files with 57439 additions and 108693 deletions
+1 -27
View File
@@ -1,39 +1,13 @@
package main
import (
"strings"
"github.com/pkg/errors"
"github.com/tarm/serial"
kcp "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/kcptun/generic"
"github.com/xtaci/serialpacket"
"github.com/xtaci/tcpraw"
)
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
// serial://NAME
if strings.HasPrefix(config.RemoteAddr, generic.SERIAL_PROTO) {
c, err := generic.ParseSerialParams(config.RemoteAddr)
if err != nil {
return nil, err
}
// open serial
s, err := serial.OpenPort(c)
if err != nil {
return nil, err
}
// wrapp to net.PacketConn
serialConn, err := serialpacket.NewConn(s)
if err != nil {
return nil, err
}
// kcp conn over serial
return kcp.NewConn("", block, config.DataShard, config.ParityShard, serialConn)
} else if config.TCP {
if config.TCP {
conn, err := tcpraw.Dial("tcp", config.RemoteAddr)
if err != nil {
return nil, errors.Wrap(err, "tcpraw.Dial()")
-38
View File
@@ -1,38 +0,0 @@
package generic
import (
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/tarm/serial"
)
const (
// serial@name@baud
// eg:
// serial@/dev/ttys002@9600
// serial@COM4@9600
SERIAL_PROTO = "serial@"
)
func ParseSerialParams(addr string) (*serial.Config, error) {
// serial://NAME
strs := strings.Split(addr, "@")
if len(strs) < 3 {
return nil, errors.Errorf("serial format error:%v", addr)
}
port := strs[1]
// parse baud
baud, err := strconv.Atoi(strs[2])
if err != nil {
return nil, err
}
config := new(serial.Config)
config.Name = port
config.Baud = baud
return config, nil
}
+5 -4
View File
@@ -4,15 +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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
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/serialpacket v0.1.1
github.com/xtaci/smux v1.5.16
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0 // indirect
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
+74 -21
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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
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.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/serialpacket v0.0.0-20220328062137-d60850dbd37f h1:qnFaiLrOPHZEwcpIesZmIljcaZedg/6LWKMLFLlkkVI=
github.com/xtaci/serialpacket v0.0.0-20220328062137-d60850dbd37f/go.mod h1:ZGGlOTneyNdYs8rDz+1b6kb6Hd+49eHY21WeNXlCZyg=
github.com/xtaci/serialpacket v0.1.0 h1:QbrsrY6zVGSYPWPDr/iZWLDwUz2QZO/qpUiytYgoeJo=
github.com/xtaci/serialpacket v0.1.0/go.mod h1:ZGGlOTneyNdYs8rDz+1b6kb6Hd+49eHY21WeNXlCZyg=
github.com/xtaci/serialpacket v0.1.1 h1:IF/hvhtMyk8RlcdJJ3ujlCRm9oZf5OrYJPMgl+lUFxM=
github.com/xtaci/serialpacket v0.1.1/go.mod h1:ZGGlOTneyNdYs8rDz+1b6kb6Hd+49eHY21WeNXlCZyg=
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=
@@ -54,49 +72,84 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
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/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-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0 h1:G6WAvvcMaaFYQhMbC0L5ZWNExEcJ3j3yFTxx4mwOHtM=
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0/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=
+13 -35
View File
@@ -10,17 +10,14 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"strings"
"sync"
"time"
"golang.org/x/crypto/pbkdf2"
"github.com/tarm/serial"
"github.com/urfave/cli"
kcp "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/kcptun/generic"
"github.com/xtaci/serialpacket"
"github.com/xtaci/smux"
"github.com/xtaci/tcpraw"
)
@@ -433,41 +430,22 @@ func main() {
}
}
var listener *kcp.Listener
if strings.HasPrefix(config.Listen, generic.SERIAL_PROTO) {
c, err := generic.ParseSerialParams(config.Listen)
checkError(err)
// open serial
s, err := serial.OpenPort(c)
checkError(err)
// wrapp to net.PacketConn
serialConn, err := serialpacket.NewConn(s)
checkError(err)
// kcp conn over serial
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, serialConn)
checkError(err)
listener = lis
} else if config.TCP { // tcp dual stack
conn, err := tcpraw.Listen("tcp", config.Listen)
checkError(err)
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
checkError(err)
listener = lis
} else {
// udp stack
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
checkError(err)
listener = lis
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)
}
}
// udp stack
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
checkError(err)
wg.Add(1)
go loop(listener)
go loop(lis)
wg.Wait()
return nil
}
-24
View File
@@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
-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 .
-35
View File
@@ -1,35 +0,0 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2015- Klaus Post & Contributors.
Email: klauspost@gmail.com
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Klaus Post
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-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
-42
View File
@@ -1,42 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build 386,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORL CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+4(FP)
MOVL BX, ebx+8(FP)
MOVL CX, ecx+12(FP)
MOVL DX, edx+16(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+4(FP)
MOVL DX, edx+8(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
-42
View File
@@ -1,42 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build amd64,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORQ CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmXgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
-26
View File
@@ -1,26 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
// func getMidr
TEXT ·getMidr(SB), 7, $0
WORD $0xd5380000 // mrs x0, midr_el1 /* Main ID Register */
MOVD R0, midr+0(FP)
RET
// func getProcFeatures
TEXT ·getProcFeatures(SB), 7, $0
WORD $0xd5380400 // mrs x0, id_aa64pfr0_el1 /* Processor Feature Register 0 */
MOVD R0, procFeatures+0(FP)
RET
// func getInstAttributes
TEXT ·getInstAttributes(SB), 7, $0
WORD $0xd5380600 // mrs x0, id_aa64isar0_el1 /* Instruction Set Attribute Register 0 */
WORD $0xd5380621 // mrs x1, id_aa64isar1_el1 /* Instruction Set Attribute Register 1 */
MOVD R0, instAttrReg0+0(FP)
MOVD R1, instAttrReg1+8(FP)
RET
-219
View File
@@ -1,219 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo,!noasm,!appengine
package cpuid
func getMidr() (midr uint64)
func getProcFeatures() (procFeatures uint64)
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(c *CPUInfo) {
// ARM64 disabled for now.
if true {
return
}
// midr := getMidr()
// MIDR_EL1 - Main ID Register
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | Implementer | [31-24] | y |
// |--------------------------------------------------|
// | Variant | [23-20] | y |
// |--------------------------------------------------|
// | Architecture | [19-16] | y |
// |--------------------------------------------------|
// | PartNum | [15-4] | y |
// |--------------------------------------------------|
// | 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)
procFeatures := getProcFeatures()
// ID_AA64PFR0_EL1 - Processor Feature Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | DIT | [51-48] | y |
// |--------------------------------------------------|
// | SVE | [35-32] | y |
// |--------------------------------------------------|
// | GIC | [27-24] | n |
// |--------------------------------------------------|
// | AdvSIMD | [23-20] | y |
// |--------------------------------------------------|
// | FP | [19-16] | y |
// |--------------------------------------------------|
// | EL3 | [15-12] | n |
// |--------------------------------------------------|
// | EL2 | [11-8] | n |
// |--------------------------------------------------|
// | EL1 | [7-4] | n |
// |--------------------------------------------------|
// | EL0 | [3-0] | n |
// x--------------------------------------------------x
var f ArmFlags
// if procFeatures&(0xf<<48) != 0 {
// fmt.Println("DIT")
// }
if procFeatures&(0xf<<32) != 0 {
f |= 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
}
instAttrReg0, instAttrReg1 := getInstAttributes()
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
//
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | TS | [55-52] | y |
// |--------------------------------------------------|
// | FHM | [51-48] | y |
// |--------------------------------------------------|
// | DP | [47-44] | y |
// |--------------------------------------------------|
// | SM4 | [43-40] | y |
// |--------------------------------------------------|
// | SM3 | [39-36] | y |
// |--------------------------------------------------|
// | SHA3 | [35-32] | y |
// |--------------------------------------------------|
// | RDM | [31-28] | y |
// |--------------------------------------------------|
// | ATOMICS | [23-20] | y |
// |--------------------------------------------------|
// | CRC32 | [19-16] | y |
// |--------------------------------------------------|
// | SHA2 | [15-12] | y |
// |--------------------------------------------------|
// | SHA1 | [11-8] | y |
// |--------------------------------------------------|
// | AES | [7-4] | y |
// x--------------------------------------------------x
// if instAttrReg0&(0xf<<52) != 0 {
// fmt.Println("TS")
// }
// 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
}
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
//
// ID_AA64ISAR1_EL1 - Instruction set attribute register 1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | GPI | [31-28] | y |
// |--------------------------------------------------|
// | GPA | [27-24] | y |
// |--------------------------------------------------|
// | LRCPC | [23-20] | y |
// |--------------------------------------------------|
// | FCMA | [19-16] | y |
// |--------------------------------------------------|
// | JSCVT | [15-12] | y |
// |--------------------------------------------------|
// | API | [11-8] | y |
// |--------------------------------------------------|
// | APA | [7-4] | y |
// |--------------------------------------------------|
// | DPB | [3-0] | y |
// x--------------------------------------------------x
// 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
}
// 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
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build 386,!gccgo,!noasm amd64,!gccgo,!noasm,!appengine
package cpuid
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 initCPU() {
cpuid = asmCpuid
cpuidex = asmCpuidex
xgetbv = asmXgetbv
rdtscpAsm = asmRdtscpAsm
}
func addInfo(c *CPUInfo) {
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.ThreadsPerCore = threadsPerCore()
c.LogicalCores = logicalCores()
c.PhysicalCores = physicalCores()
c.VendorID, c.VendorString = vendorID()
c.Hz = hertz(c.BrandName)
c.cacheSize()
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build !amd64,!386,!arm64 gccgo noasm appengine
package cpuid
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(info *CPUInfo) {}
-3
View File
@@ -1,3 +0,0 @@
module github.com/klauspost/cpuid
go 1.12
-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
-14
View File
@@ -1,14 +0,0 @@
sudo: false
language: go
go:
- "1.9"
- "1.10"
- "1.11"
- tip
env:
- GOOS=linux CGO=1
- GOOS=linux CGO=0
- GOOS=linux GOARCH=arm
- GOOS=linux GOARCH=mips
- GOOS=linux GOARCH=mipsle
- GOOS=windows GOARCH=386
-27
View File
@@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. 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 Google Inc. 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
OWNER 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.
-82
View File
@@ -1,82 +0,0 @@
[![GoDoc](https://godoc.org/github.com/tarm/serial?status.svg)](http://godoc.org/github.com/tarm/serial)
[![Build Status](https://travis-ci.org/tarm/serial.svg?branch=master)](https://travis-ci.org/tarm/serial)
Serial
========
A Go package to allow you to read and write from the
serial port as a stream of bytes.
Details
-------
It aims to have the same API on all platforms, including windows. As
an added bonus, the windows package does not use cgo, so you can cross
compile for windows from another platform.
You can cross compile with
GOOS=windows GOARCH=386 go install github.com/tarm/serial
Currently there is very little in the way of configurability. You can
set the baud rate. Then you can Read(), Write(), or Close() the
connection. By default Read() will block until at least one byte is
returned. Write is the same.
Currently all ports are opened with 8 data bits, 1 stop bit, no
parity, no hardware flow control, and no software flow control. This
works fine for many real devices and many faux serial devices
including usb-to-serial converters and bluetooth serial ports.
You may Read() and Write() simulantiously on the same connection (from
different goroutines).
Usage
-----
```go
package main
import (
"log"
"github.com/tarm/serial"
)
func main() {
c := &serial.Config{Name: "COM45", Baud: 115200}
s, err := serial.OpenPort(c)
if err != nil {
log.Fatal(err)
}
n, err := s.Write([]byte("test"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 128)
n, err = s.Read(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("%q", buf[:n])
}
```
NonBlocking Mode
----------------
By default the returned Port reads in blocking mode. Which means
`Read()` will block until at least one byte is returned. If that's not
what you want, specify a positive ReadTimeout and the Read() will
timeout returning 0 bytes if no bytes are read. Please note that this
is the total timeout the read operation will wait and not the interval
timeout between two bytes.
```go
c := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Second * 5}
// In this mode, you will want to suppress error for read
// as 0 bytes return EOF error on Linux / POSIX
n, _ = s.Read(buf)
```
Possible Future Work
--------------------
- better tests (loopback etc)
-167
View File
@@ -1,167 +0,0 @@
/*
Goserial is a simple go package to allow you to read and write from
the serial port as a stream of bytes.
It aims to have the same API on all platforms, including windows. As
an added bonus, the windows package does not use cgo, so you can cross
compile for windows from another platform. Unfortunately goinstall
does not currently let you cross compile so you will have to do it
manually:
GOOS=windows make clean install
Currently there is very little in the way of configurability. You can
set the baud rate. Then you can Read(), Write(), or Close() the
connection. Read() will block until at least one byte is returned.
Write is the same. There is currently no exposed way to set the
timeouts, though patches are welcome.
Currently all ports are opened with 8 data bits, 1 stop bit, no
parity, no hardware flow control, and no software flow control. This
works fine for many real devices and many faux serial devices
including usb-to-serial converters and bluetooth serial ports.
You may Read() and Write() simulantiously on the same connection (from
different goroutines).
Example usage:
package main
import (
"github.com/tarm/serial"
"log"
)
func main() {
c := &serial.Config{Name: "COM5", Baud: 115200}
s, err := serial.OpenPort(c)
if err != nil {
log.Fatal(err)
}
n, err := s.Write([]byte("test"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 128)
n, err = s.Read(buf)
if err != nil {
log.Fatal(err)
}
log.Print("%q", buf[:n])
}
*/
package serial
import (
"errors"
"time"
)
const DefaultSize = 8 // Default value for Config.Size
type StopBits byte
type Parity byte
const (
Stop1 StopBits = 1
Stop1Half StopBits = 15
Stop2 StopBits = 2
)
const (
ParityNone Parity = 'N'
ParityOdd Parity = 'O'
ParityEven Parity = 'E'
ParityMark Parity = 'M' // parity bit is always 1
ParitySpace Parity = 'S' // parity bit is always 0
)
// Config contains the information needed to open a serial port.
//
// Currently few options are implemented, but more may be added in the
// future (patches welcome), so it is recommended that you create a
// new config addressing the fields by name rather than by order.
//
// For example:
//
// c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Millisecond * 500}
// or
// c1 := new(serial.Config)
// c1.Name = "/dev/tty.usbserial"
// c1.Baud = 115200
// c1.ReadTimeout = time.Millisecond * 500
//
type Config struct {
Name string
Baud int
ReadTimeout time.Duration // Total timeout
// Size is the number of data bits. If 0, DefaultSize is used.
Size byte
// Parity is the bit to use and defaults to ParityNone (no parity bit).
Parity Parity
// Number of stop bits to use. Default is 1 (1 stop bit).
StopBits StopBits
// RTSFlowControl bool
// DTRFlowControl bool
// XONFlowControl bool
// CRLFTranslate bool
}
// ErrBadSize is returned if Size is not supported.
var ErrBadSize error = errors.New("unsupported serial data size")
// ErrBadStopBits is returned if the specified StopBits setting not supported.
var ErrBadStopBits error = errors.New("unsupported stop bit setting")
// ErrBadParity is returned if the parity is not supported.
var ErrBadParity error = errors.New("unsupported parity setting")
// OpenPort opens a serial port with the specified configuration
func OpenPort(c *Config) (*Port, error) {
size, par, stop := c.Size, c.Parity, c.StopBits
if size == 0 {
size = DefaultSize
}
if par == 0 {
par = ParityNone
}
if stop == 0 {
stop = Stop1
}
return openPort(c.Name, c.Baud, size, par, stop, c.ReadTimeout)
}
// Converts the timeout values for Linux / POSIX systems
func posixTimeoutValues(readTimeout time.Duration) (vmin uint8, vtime uint8) {
const MAXUINT8 = 1<<8 - 1 // 255
// set blocking / non-blocking read
var minBytesToRead uint8 = 1
var readTimeoutInDeci int64
if readTimeout > 0 {
// EOF on zero read
minBytesToRead = 0
// convert timeout to deciseconds as expected by VTIME
readTimeoutInDeci = (readTimeout.Nanoseconds() / 1e6 / 100)
// capping the timeout
if readTimeoutInDeci < 1 {
// min possible timeout 1 Deciseconds (0.1s)
readTimeoutInDeci = 1
} else if readTimeoutInDeci > MAXUINT8 {
// max possible timeout is 255 deciseconds (25.5s)
readTimeoutInDeci = MAXUINT8
}
}
return minBytesToRead, uint8(readTimeoutInDeci)
}
// func SendBreak()
// func RegisterBreakHandler(func())
-164
View File
@@ -1,164 +0,0 @@
// +build linux
package serial
import (
"fmt"
"os"
"time"
"unsafe"
"golang.org/x/sys/unix"
)
func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, readTimeout time.Duration) (p *Port, err error) {
var bauds = map[int]uint32{
50: unix.B50,
75: unix.B75,
110: unix.B110,
134: unix.B134,
150: unix.B150,
200: unix.B200,
300: unix.B300,
600: unix.B600,
1200: unix.B1200,
1800: unix.B1800,
2400: unix.B2400,
4800: unix.B4800,
9600: unix.B9600,
19200: unix.B19200,
38400: unix.B38400,
57600: unix.B57600,
115200: unix.B115200,
230400: unix.B230400,
460800: unix.B460800,
500000: unix.B500000,
576000: unix.B576000,
921600: unix.B921600,
1000000: unix.B1000000,
1152000: unix.B1152000,
1500000: unix.B1500000,
2000000: unix.B2000000,
2500000: unix.B2500000,
3000000: unix.B3000000,
3500000: unix.B3500000,
4000000: unix.B4000000,
}
rate, ok := bauds[baud]
if !ok {
return nil, fmt.Errorf("Unrecognized baud rate")
}
f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0666)
if err != nil {
return nil, err
}
defer func() {
if err != nil && f != nil {
f.Close()
}
}()
// Base settings
cflagToUse := unix.CREAD | unix.CLOCAL | rate
switch databits {
case 5:
cflagToUse |= unix.CS5
case 6:
cflagToUse |= unix.CS6
case 7:
cflagToUse |= unix.CS7
case 8:
cflagToUse |= unix.CS8
default:
return nil, ErrBadSize
}
// Stop bits settings
switch stopbits {
case Stop1:
// default is 1 stop bit
case Stop2:
cflagToUse |= unix.CSTOPB
default:
// Don't know how to set 1.5
return nil, ErrBadStopBits
}
// Parity settings
switch parity {
case ParityNone:
// default is no parity
case ParityOdd:
cflagToUse |= unix.PARENB
cflagToUse |= unix.PARODD
case ParityEven:
cflagToUse |= unix.PARENB
default:
return nil, ErrBadParity
}
fd := f.Fd()
vmin, vtime := posixTimeoutValues(readTimeout)
t := unix.Termios{
Iflag: unix.IGNPAR,
Cflag: cflagToUse,
Ispeed: rate,
Ospeed: rate,
}
t.Cc[unix.VMIN] = vmin
t.Cc[unix.VTIME] = vtime
if _, _, errno := unix.Syscall6(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.TCSETS),
uintptr(unsafe.Pointer(&t)),
0,
0,
0,
); errno != 0 {
return nil, errno
}
if err = unix.SetNonblock(int(fd), false); err != nil {
return
}
return &Port{f: f}, nil
}
type Port struct {
// We intentionly do not use an "embedded" struct so that we
// don't export File
f *os.File
}
func (p *Port) Read(b []byte) (n int, err error) {
return p.f.Read(b)
}
func (p *Port) Write(b []byte) (n int, err error) {
return p.f.Write(b)
}
// Discards data written to the port but not transmitted,
// or data received but not read
func (p *Port) Flush() error {
const TCFLSH = 0x540B
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(p.f.Fd()),
uintptr(TCFLSH),
uintptr(unix.TCIOFLUSH),
)
if errno == 0 {
return nil
}
return errno
}
func (p *Port) Close() (err error) {
return p.f.Close()
}
-197
View File
@@ -1,197 +0,0 @@
// +build !windows,!linux,cgo
package serial
// #include <termios.h>
// #include <unistd.h>
import "C"
// TODO: Maybe change to using syscall package + ioctl instead of cgo
import (
"errors"
"fmt"
"os"
"syscall"
"time"
//"unsafe"
)
func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, readTimeout time.Duration) (p *Port, err error) {
f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
if err != nil {
return
}
fd := C.int(f.Fd())
if C.isatty(fd) != 1 {
f.Close()
return nil, errors.New("File is not a tty")
}
var st C.struct_termios
_, err = C.tcgetattr(fd, &st)
if err != nil {
f.Close()
return nil, err
}
var speed C.speed_t
switch baud {
case 115200:
speed = C.B115200
case 57600:
speed = C.B57600
case 38400:
speed = C.B38400
case 19200:
speed = C.B19200
case 9600:
speed = C.B9600
case 4800:
speed = C.B4800
case 2400:
speed = C.B2400
case 1200:
speed = C.B1200
case 600:
speed = C.B600
case 300:
speed = C.B300
case 200:
speed = C.B200
case 150:
speed = C.B150
case 134:
speed = C.B134
case 110:
speed = C.B110
case 75:
speed = C.B75
case 50:
speed = C.B50
default:
f.Close()
return nil, fmt.Errorf("Unknown baud rate %v", baud)
}
_, err = C.cfsetispeed(&st, speed)
if err != nil {
f.Close()
return nil, err
}
_, err = C.cfsetospeed(&st, speed)
if err != nil {
f.Close()
return nil, err
}
// Turn off break interrupts, CR->NL, Parity checks, strip, and IXON
st.c_iflag &= ^C.tcflag_t(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXOFF | C.IXON | C.PARMRK)
// Select local mode, turn off parity, set to 8 bits
st.c_cflag &= ^C.tcflag_t(C.CSIZE | C.PARENB)
st.c_cflag |= (C.CLOCAL | C.CREAD)
// databits
switch databits {
case 5:
st.c_cflag |= C.CS5
case 6:
st.c_cflag |= C.CS6
case 7:
st.c_cflag |= C.CS7
case 8:
st.c_cflag |= C.CS8
default:
return nil, ErrBadSize
}
// Parity settings
switch parity {
case ParityNone:
// default is no parity
case ParityOdd:
st.c_cflag |= C.PARENB
st.c_cflag |= C.PARODD
case ParityEven:
st.c_cflag |= C.PARENB
st.c_cflag &= ^C.tcflag_t(C.PARODD)
default:
return nil, ErrBadParity
}
// Stop bits settings
switch stopbits {
case Stop1:
// as is, default is 1 bit
case Stop2:
st.c_cflag |= C.CSTOPB
default:
return nil, ErrBadStopBits
}
// Select raw mode
st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG)
st.c_oflag &= ^C.tcflag_t(C.OPOST)
// set blocking / non-blocking read
/*
* http://man7.org/linux/man-pages/man3/termios.3.html
* - Supports blocking read and read with timeout operations
*/
vmin, vtime := posixTimeoutValues(readTimeout)
st.c_cc[C.VMIN] = C.cc_t(vmin)
st.c_cc[C.VTIME] = C.cc_t(vtime)
_, err = C.tcsetattr(fd, C.TCSANOW, &st)
if err != nil {
f.Close()
return nil, err
}
//fmt.Println("Tweaking", name)
r1, _, e := syscall.Syscall(syscall.SYS_FCNTL,
uintptr(f.Fd()),
uintptr(syscall.F_SETFL),
uintptr(0))
if e != 0 || r1 != 0 {
s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1)
f.Close()
return nil, errors.New(s)
}
/*
r1, _, e = syscall.Syscall(syscall.SYS_IOCTL,
uintptr(f.Fd()),
uintptr(0x80045402), // IOSSIOSPEED
uintptr(unsafe.Pointer(&baud)));
if e != 0 || r1 != 0 {
s := fmt.Sprint("Baudrate syscall error:", e, r1)
f.Close()
return nil, os.NewError(s)
}
*/
return &Port{f: f}, nil
}
type Port struct {
// We intentionly do not use an "embedded" struct so that we
// don't export File
f *os.File
}
func (p *Port) Read(b []byte) (n int, err error) {
return p.f.Read(b)
}
func (p *Port) Write(b []byte) (n int, err error) {
return p.f.Write(b)
}
// Discards data written to the port but not transmitted,
// or data received but not read
func (p *Port) Flush() error {
_, err := C.tcflush(C.int(p.f.Fd()), C.TCIOFLUSH)
return err
}
func (p *Port) Close() (err error) {
return p.f.Close()
}
-327
View File
@@ -1,327 +0,0 @@
// +build windows
package serial
import (
"fmt"
"os"
"sync"
"syscall"
"time"
"unsafe"
)
type Port struct {
f *os.File
fd syscall.Handle
rl sync.Mutex
wl sync.Mutex
ro *syscall.Overlapped
wo *syscall.Overlapped
}
type structDCB struct {
DCBlength, BaudRate uint32
flags [4]byte
wReserved, XonLim, XoffLim uint16
ByteSize, Parity, StopBits byte
XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
wReserved1 uint16
}
type structTimeouts struct {
ReadIntervalTimeout uint32
ReadTotalTimeoutMultiplier uint32
ReadTotalTimeoutConstant uint32
WriteTotalTimeoutMultiplier uint32
WriteTotalTimeoutConstant uint32
}
func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, readTimeout time.Duration) (p *Port, err error) {
if len(name) > 0 && name[0] != '\\' {
name = "\\\\.\\" + name
}
h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
0,
nil,
syscall.OPEN_EXISTING,
syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
0)
if err != nil {
return nil, err
}
f := os.NewFile(uintptr(h), name)
defer func() {
if err != nil {
f.Close()
}
}()
if err = setCommState(h, baud, databits, parity, stopbits); err != nil {
return nil, err
}
if err = setupComm(h, 64, 64); err != nil {
return nil, err
}
if err = setCommTimeouts(h, readTimeout); err != nil {
return nil, err
}
if err = setCommMask(h); err != nil {
return nil, err
}
ro, err := newOverlapped()
if err != nil {
return nil, err
}
wo, err := newOverlapped()
if err != nil {
return nil, err
}
port := new(Port)
port.f = f
port.fd = h
port.ro = ro
port.wo = wo
return port, nil
}
func (p *Port) Close() error {
return p.f.Close()
}
func (p *Port) Write(buf []byte) (int, error) {
p.wl.Lock()
defer p.wl.Unlock()
if err := resetEvent(p.wo.HEvent); err != nil {
return 0, err
}
var n uint32
err := syscall.WriteFile(p.fd, buf, &n, p.wo)
if err != nil && err != syscall.ERROR_IO_PENDING {
return int(n), err
}
return getOverlappedResult(p.fd, p.wo)
}
func (p *Port) Read(buf []byte) (int, error) {
if p == nil || p.f == nil {
return 0, fmt.Errorf("Invalid port on read")
}
p.rl.Lock()
defer p.rl.Unlock()
if err := resetEvent(p.ro.HEvent); err != nil {
return 0, err
}
var done uint32
err := syscall.ReadFile(p.fd, buf, &done, p.ro)
if err != nil && err != syscall.ERROR_IO_PENDING {
return int(done), err
}
return getOverlappedResult(p.fd, p.ro)
}
// Discards data written to the port but not transmitted,
// or data received but not read
func (p *Port) Flush() error {
return purgeComm(p.fd)
}
var (
nSetCommState,
nSetCommTimeouts,
nSetCommMask,
nSetupComm,
nGetOverlappedResult,
nCreateEvent,
nResetEvent,
nPurgeComm,
nFlushFileBuffers uintptr
)
func init() {
k32, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
panic("LoadLibrary " + err.Error())
}
defer syscall.FreeLibrary(k32)
nSetCommState = getProcAddr(k32, "SetCommState")
nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
nSetCommMask = getProcAddr(k32, "SetCommMask")
nSetupComm = getProcAddr(k32, "SetupComm")
nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
nCreateEvent = getProcAddr(k32, "CreateEventW")
nResetEvent = getProcAddr(k32, "ResetEvent")
nPurgeComm = getProcAddr(k32, "PurgeComm")
nFlushFileBuffers = getProcAddr(k32, "FlushFileBuffers")
}
func getProcAddr(lib syscall.Handle, name string) uintptr {
addr, err := syscall.GetProcAddress(lib, name)
if err != nil {
panic(name + " " + err.Error())
}
return addr
}
func setCommState(h syscall.Handle, baud int, databits byte, parity Parity, stopbits StopBits) error {
var params structDCB
params.DCBlength = uint32(unsafe.Sizeof(params))
params.flags[0] = 0x01 // fBinary
params.flags[0] |= 0x10 // Assert DSR
params.BaudRate = uint32(baud)
params.ByteSize = databits
switch parity {
case ParityNone:
params.Parity = 0
case ParityOdd:
params.Parity = 1
case ParityEven:
params.Parity = 2
case ParityMark:
params.Parity = 3
case ParitySpace:
params.Parity = 4
default:
return ErrBadParity
}
switch stopbits {
case Stop1:
params.StopBits = 0
case Stop1Half:
params.StopBits = 1
case Stop2:
params.StopBits = 2
default:
return ErrBadStopBits
}
r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(&params)), 0)
if r == 0 {
return err
}
return nil
}
func setCommTimeouts(h syscall.Handle, readTimeout time.Duration) error {
var timeouts structTimeouts
const MAXDWORD = 1<<32 - 1
// blocking read by default
var timeoutMs int64 = MAXDWORD - 1
if readTimeout > 0 {
// non-blocking read
timeoutMs = readTimeout.Nanoseconds() / 1e6
if timeoutMs < 1 {
timeoutMs = 1
} else if timeoutMs > MAXDWORD-1 {
timeoutMs = MAXDWORD - 1
}
}
/* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx
For blocking I/O see below:
Remarks:
If an application sets ReadIntervalTimeout and
ReadTotalTimeoutMultiplier to MAXDWORD and sets
ReadTotalTimeoutConstant to a value greater than zero and
less than MAXDWORD, one of the following occurs when the
ReadFile function is called:
If there are any bytes in the input buffer, ReadFile returns
immediately with the bytes in the buffer.
If there are no bytes in the input buffer, ReadFile waits
until a byte arrives and then returns immediately.
If no bytes arrive within the time specified by
ReadTotalTimeoutConstant, ReadFile times out.
*/
timeouts.ReadIntervalTimeout = MAXDWORD
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
timeouts.ReadTotalTimeoutConstant = uint32(timeoutMs)
r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
if r == 0 {
return err
}
return nil
}
func setupComm(h syscall.Handle, in, out int) error {
r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out))
if r == 0 {
return err
}
return nil
}
func setCommMask(h syscall.Handle) error {
const EV_RXCHAR = 0x0001
r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0)
if r == 0 {
return err
}
return nil
}
func resetEvent(h syscall.Handle) error {
r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0)
if r == 0 {
return err
}
return nil
}
func purgeComm(h syscall.Handle) error {
const PURGE_TXABORT = 0x0001
const PURGE_RXABORT = 0x0002
const PURGE_TXCLEAR = 0x0004
const PURGE_RXCLEAR = 0x0008
r, _, err := syscall.Syscall(nPurgeComm, 2, uintptr(h),
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR, 0)
if r == 0 {
return err
}
return nil
}
func newOverlapped() (*syscall.Overlapped, error) {
var overlapped syscall.Overlapped
r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0)
if r == 0 {
return nil, err
}
overlapped.HEvent = syscall.Handle(r)
return &overlapped, nil
}
func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) {
var n int
r, _, err := syscall.Syscall6(nGetOverlappedResult, 4,
uintptr(h),
uintptr(unsafe.Pointer(overlapped)),
uintptr(unsafe.Pointer(&n)), 1, 0, 0)
if r == 0 {
return n, err
}
return n, nil
}
+242 -101
View File
@@ -12,29 +12,23 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
sm4 acceration
sm4 acceleration
modified by Jack, 2017 Oct
*/
package sm4
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"os"
"strconv"
)
const BlockSize = 16
var IV=make([]byte,BlockSize)
type SM4Key []byte
type KeySizeError int
// Cipher is an instance of SM4 encryption.
type Sm4Cipher struct {
subkeys []uint32
@@ -234,101 +228,10 @@ func generateSubKeys(key []byte) []uint32 {
return subkeys
}
func EncryptBlock(key SM4Key, dst, src []byte) {
subkeys := generateSubKeys(key)
cryptBlock(subkeys, make([]uint32, 4), make([]byte, 16), dst, src, false)
}
func DecryptBlock(key SM4Key, dst, src []byte) {
subkeys := generateSubKeys(key)
cryptBlock(subkeys, make([]uint32, 4), make([]byte, 16), dst, src, true)
}
func ReadKeyFromMem(data []byte, pwd []byte) (SM4Key, error) {
block, _ := pem.Decode(data)
if block == nil {
return nil, errors.New("SM4: pem decode failed")
}
if x509.IsEncryptedPEMBlock(block) {
if block.Type != "SM4 ENCRYPTED KEY" {
return nil, errors.New("SM4: unknown type")
}
if pwd == nil {
return nil, errors.New("SM4: need passwd")
}
data, err := x509.DecryptPEMBlock(block, pwd)
if err != nil {
return nil, err
}
return data, nil
}
if block.Type != "SM4 KEY" {
return nil, errors.New("SM4: unknown type")
}
return block.Bytes, nil
}
func ReadKeyFromPem(FileName string, pwd []byte) (SM4Key, error) {
data, err := ioutil.ReadFile(FileName)
if err != nil {
return nil, err
}
return ReadKeyFromMem(data, pwd)
}
func WriteKeytoMem(key SM4Key, pwd []byte) ([]byte, error) {
if pwd != nil {
block, err := x509.EncryptPEMBlock(rand.Reader,
"SM4 ENCRYPTED KEY", key, pwd, x509.PEMCipherAES256)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(block), nil
} else {
block := &pem.Block{
Type: "SM4 KEY",
Bytes: key,
}
return pem.EncodeToMemory(block), nil
}
}
func WriteKeyToPem(FileName string, key SM4Key, pwd []byte) (bool, error) {
var block *pem.Block
if pwd != nil {
var err error
block, err = x509.EncryptPEMBlock(rand.Reader,
"SM4 ENCRYPTED KEY", key, pwd, x509.PEMCipherAES256)
if err != nil {
return false, err
}
} else {
block = &pem.Block{
Type: "SM4 KEY",
Bytes: key,
}
}
file, err := os.Create(FileName)
if err != nil {
return false, err
}
defer file.Close()
err = pem.Encode(file, block)
if err != nil {
return false, nil
}
return true, nil
}
func (k KeySizeError) Error() string {
return "SM4: invalid key size " + strconv.Itoa(int(k))
}
// NewCipher creates and returns a new cipher.Block.
func NewCipher(key []byte) (cipher.Block, error) {
if len(key) != BlockSize {
return nil, KeySizeError(len(key))
return nil, errors.New("SM4: invalid key size " + strconv.Itoa(len(key)))
}
c := new(Sm4Cipher)
c.subkeys = generateSubKeys(key)
@@ -345,6 +248,244 @@ func (c *Sm4Cipher) Encrypt(dst, src []byte) {
cryptBlock(c.subkeys, c.block1, c.block2, dst, src, false)
}
func (c *Sm4Cipher) Decrypt(dst, src []byte) {
cryptBlock(c.subkeys, c.block1, c.block2, dst, src, true)
}
func xor(in, iv []byte) (out []byte) {
if len(in) != len(iv) {
return nil
}
out = make([]byte, len(in))
for i := 0; i < len(in); i++ {
out[i] = in[i] ^ iv[i]
}
return
}
func pkcs7Padding(src []byte) []byte {
padding := BlockSize - len(src)%BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func pkcs7UnPadding(src []byte) ([]byte, error) {
length := len(src)
unpadding := int(src[length-1])
if unpadding > BlockSize || unpadding == 0 {
return nil, errors.New("Invalid pkcs7 padding (unpadding > BlockSize || unpadding == 0)")
}
pad := src[len(src)-unpadding:]
for i := 0; i < unpadding; i++ {
if pad[i] != byte(unpadding) {
return nil, errors.New("Invalid pkcs7 padding (pad[i] != unpadding)")
}
}
return src[:(length - unpadding)], nil
}
func SetIV(iv []byte)error{
if len(iv)!=BlockSize{
return errors.New("SM4: invalid iv size")
}
IV=iv
return nil
}
func Sm4Cbc(key []byte, in []byte, mode bool) (out []byte, err error) {
if len(key) != BlockSize {
return nil, errors.New("SM4: invalid key size " + strconv.Itoa(len(key)))
}
var inData []byte
if mode {
inData = pkcs7Padding(in)
} else {
inData = in
}
iv:=make([]byte,BlockSize)
copy(iv,IV)
out = make([]byte, len(inData))
c, err := NewCipher(key)
if err != nil {
panic(err)
}
if mode {
for i := 0; i < len(inData)/16; i++ {
in_tmp := xor(inData[i*16:i*16+16], iv)
out_tmp := make([]byte, 16)
c.Encrypt(out_tmp, in_tmp)
copy(out[i*16:i*16+16], out_tmp)
iv = out_tmp
}
} else {
for i := 0; i < len(inData)/16; i++ {
in_tmp := inData[i*16 : i*16+16]
out_tmp := make([]byte, 16)
c.Decrypt(out_tmp, in_tmp)
out_tmp = xor(out_tmp, iv)
copy(out[i*16:i*16+16], out_tmp)
iv = in_tmp
}
out, _ = pkcs7UnPadding(out)
}
return out, nil
}
func Sm4Ecb(key []byte, in []byte, mode bool) (out []byte, err error) {
if len(key) != BlockSize {
return nil, errors.New("SM4: invalid key size " + strconv.Itoa(len(key)))
}
var inData []byte
if mode {
inData = pkcs7Padding(in)
} else {
inData = in
}
out = make([]byte, len(inData))
c, err := NewCipher(key)
if err != nil {
panic(err)
}
if mode {
for i := 0; i < len(inData)/16; i++ {
in_tmp := inData[i*16 : i*16+16]
out_tmp := make([]byte, 16)
c.Encrypt(out_tmp, in_tmp)
copy(out[i*16:i*16+16], out_tmp)
}
} else {
for i := 0; i < len(inData)/16; i++ {
in_tmp := inData[i*16 : i*16+16]
out_tmp := make([]byte, 16)
c.Decrypt(out_tmp, in_tmp)
copy(out[i*16:i*16+16], out_tmp)
}
out, _ = pkcs7UnPadding(out)
}
return out, nil
}
//密码反馈模式(Cipher FeedBack (CFB)
//https://blog.csdn.net/zy_strive_2012/article/details/102520356
//https://blog.csdn.net/sinat_23338865/article/details/72869841
func Sm4CFB(key []byte, in []byte, mode bool) (out []byte, err error) {
if len(key) != BlockSize {
return nil, errors.New("SM4: invalid key size " + strconv.Itoa(len(key)))
}
var inData []byte
if mode {
inData = pkcs7Padding(in)
} else {
inData = in
}
out = make([]byte, len(inData))
c, err := NewCipher(key)
if err != nil {
panic(err)
}
K := make([]byte, BlockSize)
cipherBlock := make([]byte, BlockSize)
plainBlock := make([]byte, BlockSize)
if mode { //加密
for i := 0; i < len(inData)/16; i++ {
if i == 0 {
c.Encrypt(K, IV)
cipherBlock = xor(K[:BlockSize], inData[i*16:i*16+16])
copy(out[i*16:i*16+16], cipherBlock)
//copy(cipherBlock,out_tmp)
continue
}
c.Encrypt(K, cipherBlock)
cipherBlock = xor(K[:BlockSize], inData[i*16:i*16+16])
copy(out[i*16:i*16+16], cipherBlock)
//copy(cipherBlock,out_tmp)
}
} else { //解密
var i int = 0
for ; i < len(inData)/16; i++ {
if i == 0 {
c.Encrypt(K, IV) //这里是加密,而不是调用解密方法Decrypt
plainBlock = xor(K[:BlockSize], inData[i*16:i*16+16]) //获取明文分组
copy(out[i*16:i*16+16], plainBlock)
continue
}
c.Encrypt(K, inData[(i-1)*16:(i-1)*16+16])
plainBlock = xor(K[:BlockSize], inData[i*16:i*16+16]) //获取明文分组
copy(out[i*16:i*16+16], plainBlock)
}
out, _ = pkcs7UnPadding(out)
}
return out, nil
}
//输出反馈模式(Output feedback, OFB
//https://blog.csdn.net/chengqiuming/article/details/82390910
//https://blog.csdn.net/sinat_23338865/article/details/72869841
func Sm4OFB(key []byte, in []byte, mode bool) (out []byte, err error) {
if len(key) != BlockSize {
return nil, errors.New("SM4: invalid key size " + strconv.Itoa(len(key)))
}
var inData []byte
if mode {
inData = pkcs7Padding(in)
} else {
inData = in
}
out = make([]byte, len(inData))
c, err := NewCipher(key)
if err != nil {
panic(err)
}
K := make([]byte, BlockSize)
cipherBlock := make([]byte, BlockSize)
plainBlock := make([]byte, BlockSize)
shiftIV := make([]byte, BlockSize)
if mode { //加密
for i := 0; i < len(inData)/16; i++ {
if i == 0 {
c.Encrypt(K, IV)
cipherBlock = xor(K[:BlockSize], inData[i*16:i*16+16])
copy(out[i*16:i*16+16], cipherBlock)
copy(shiftIV, K[:BlockSize])
continue
}
c.Encrypt(K, shiftIV)
cipherBlock = xor(K[:BlockSize], inData[i*16:i*16+16])
copy(out[i*16:i*16+16], cipherBlock)
copy(shiftIV, K[:BlockSize])
}
} else { //解密
for i := 0; i < len(inData)/16; i++ {
if i == 0 {
c.Encrypt(K, IV) //这里是加密,而不是调用解密方法Decrypt
plainBlock = xor(K[:BlockSize], inData[i*16:i*16+16]) //获取明文分组
copy(out[i*16:i*16+16], plainBlock)
copy(shiftIV, K[:BlockSize])
continue
}
c.Encrypt(K, shiftIV)
plainBlock = xor(K[:BlockSize], inData[i*16:i*16+16]) //获取明文分组
copy(out[i*16:i*16+16], plainBlock)
copy(shiftIV, K[:BlockSize])
}
out, _ = pkcs7UnPadding(out)
}
return out, nil
}
-15
View File
@@ -1,15 +0,0 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 xtaci
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-16
View File
@@ -1,16 +0,0 @@
# serialpacket
SerialPacket is a net.PacketConn implementation over RS232.
It's designed to work with [kcp-go](https://github.com/xtaci/kcp-go) to provide reliable transmission over [LoRa](https://en.wikipedia.org/wiki/LoRa) or other noisy channels.
Test:
```
$ socat -d -d pty,raw,echo=0 pty,raw,echo=0
2022/03/27 22:48:28 socat[14099] N PTY is /dev/pts/5
2022/03/27 22:48:28 socat[14099] N PTY is /dev/pts/6
2022/03/27 22:48:28 socat[14099] N starting data transfer loop with FDs [5,5] and [7,7]
$ export PORT1="/dev/pts/5"
$ export PORT1="/dev/pts/6"
```
-24
View File
@@ -1,24 +0,0 @@
module github.com/xtaci/serialpacket
go 1.17
require (
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
)
require (
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20220325203850-36772127a21f // indirect
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
-78
View File
@@ -1,78 +0,0 @@
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/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/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/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/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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
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/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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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-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/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/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-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

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