parent
6e49867d6f
commit
dc3bc54468
@ -1,5 +1,5 @@
|
||||
package go_library
|
||||
|
||||
func Version() string {
|
||||
return "1.0.155"
|
||||
return "1.0.156"
|
||||
}
|
||||
|
@ -0,0 +1,329 @@
|
||||
/**
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`sort`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
type nodeChunk [_DEFAULT_NODE_CAP]Node
|
||||
|
||||
type linkedNodes struct {
|
||||
head nodeChunk
|
||||
tail []*nodeChunk
|
||||
size int
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Cap() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return (len(self.tail)+1)*_DEFAULT_NODE_CAP
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Len() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return self.size
|
||||
}
|
||||
|
||||
func (self *linkedNodes) At(i int) (*Node) {
|
||||
if self == nil {
|
||||
return nil
|
||||
}
|
||||
if i >= 0 && i<self.size && i < _DEFAULT_NODE_CAP {
|
||||
return &self.head[i]
|
||||
} else if i >= _DEFAULT_NODE_CAP && i<self.size {
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < len(self.tail) {
|
||||
return &self.tail[a][b]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Add(v Node) {
|
||||
if self.size < _DEFAULT_NODE_CAP {
|
||||
self.head[self.size] = v
|
||||
self.size++
|
||||
return
|
||||
}
|
||||
|
||||
a, b, c := self.size/_DEFAULT_NODE_CAP-1 , self.size%_DEFAULT_NODE_CAP, cap(self.tail)
|
||||
if a - c >= 0 {
|
||||
c += 1 + c>>_APPEND_GROW_SHIFT
|
||||
tmp := make([]*nodeChunk, a + 1, c)
|
||||
copy(tmp, self.tail)
|
||||
self.tail = tmp
|
||||
} else if a >= len(self.tail) {
|
||||
self.tail = self.tail[:a+1]
|
||||
}
|
||||
|
||||
var n = &self.tail[a]
|
||||
if *n == nil {
|
||||
*n = new(nodeChunk)
|
||||
}
|
||||
(*n)[b] = v
|
||||
self.size++
|
||||
}
|
||||
|
||||
func (self *linkedNodes) ToSlice(con []Node) {
|
||||
if len(con) < self.size {
|
||||
return
|
||||
}
|
||||
i := (self.size-1)
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
copy(con, self.head[:b+1])
|
||||
return
|
||||
} else {
|
||||
copy(con, self.head[:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
copy(con, self.tail[i][:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
copy(con, self.tail[a][:b+1])
|
||||
}
|
||||
|
||||
func (self *linkedNodes) FromSlice(con []Node) {
|
||||
self.size = len(con)
|
||||
i := self.size-1
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
copy(self.head[:b+1], con)
|
||||
return
|
||||
} else {
|
||||
copy(self.head[:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
if cap(self.tail) <= a {
|
||||
c := (a+1) + (a+1)>>_APPEND_GROW_SHIFT
|
||||
self.tail = make([]*nodeChunk, a+1, c)
|
||||
}
|
||||
self.tail = self.tail[:a+1]
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
self.tail[i] = new(nodeChunk)
|
||||
copy(self.tail[i][:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
self.tail[a] = new(nodeChunk)
|
||||
copy(self.tail[a][:b+1], con)
|
||||
}
|
||||
|
||||
type pairChunk [_DEFAULT_NODE_CAP]Pair
|
||||
|
||||
type linkedPairs struct {
|
||||
head pairChunk
|
||||
tail []*pairChunk
|
||||
size int
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Cap() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return (len(self.tail)+1)*_DEFAULT_NODE_CAP
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Len() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return self.size
|
||||
}
|
||||
|
||||
func (self *linkedPairs) At(i int) *Pair {
|
||||
if self == nil {
|
||||
return nil
|
||||
}
|
||||
if i >= 0 && i < _DEFAULT_NODE_CAP && i<self.size {
|
||||
return &self.head[i]
|
||||
} else if i >= _DEFAULT_NODE_CAP && i<self.size {
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < len(self.tail) {
|
||||
return &self.tail[a][b]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Add(v Pair) {
|
||||
if self.size < _DEFAULT_NODE_CAP {
|
||||
self.head[self.size] = v
|
||||
self.size++
|
||||
return
|
||||
}
|
||||
|
||||
a, b, c := self.size/_DEFAULT_NODE_CAP-1 , self.size%_DEFAULT_NODE_CAP, cap(self.tail)
|
||||
if a - c >= 0 {
|
||||
c += 1 + c>>_APPEND_GROW_SHIFT
|
||||
tmp := make([]*pairChunk, a + 1, c)
|
||||
copy(tmp, self.tail)
|
||||
self.tail = tmp
|
||||
} else if a >= len(self.tail) {
|
||||
self.tail = self.tail[:a+1]
|
||||
}
|
||||
|
||||
var n = &self.tail[a]
|
||||
if *n == nil {
|
||||
*n = new(pairChunk)
|
||||
}
|
||||
(*n)[b] = v
|
||||
self.size++
|
||||
}
|
||||
|
||||
// linear search
|
||||
func (self *linkedPairs) Get(key string) (*Pair, int) {
|
||||
for i:=0; i<self.size; i++ {
|
||||
if n := self.At(i); n.Key == key {
|
||||
return n, i
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func (self *linkedPairs) ToSlice(con []Pair) {
|
||||
if len(con) < self.size {
|
||||
return
|
||||
}
|
||||
i := self.size-1
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
|
||||
if a < 0 {
|
||||
copy(con, self.head[:b+1])
|
||||
return
|
||||
} else {
|
||||
copy(con, self.head[:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
copy(con, self.tail[i][:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
copy(con, self.tail[a][:b+1])
|
||||
}
|
||||
|
||||
func (self *linkedPairs) ToMap(con map[string]Node) {
|
||||
for i:=0; i<self.size; i++ {
|
||||
n := self.At(i)
|
||||
con[n.Key] = n.Value
|
||||
}
|
||||
}
|
||||
|
||||
func (self *linkedPairs) FromSlice(con []Pair) {
|
||||
self.size = len(con)
|
||||
i := self.size-1
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
copy(self.head[:b+1], con)
|
||||
return
|
||||
} else {
|
||||
copy(self.head[:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
if cap(self.tail) <= a {
|
||||
c := (a+1) + (a+1)>>_APPEND_GROW_SHIFT
|
||||
self.tail = make([]*pairChunk, a+1, c)
|
||||
}
|
||||
self.tail = self.tail[:a+1]
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
self.tail[i] = new(pairChunk)
|
||||
copy(self.tail[i][:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
self.tail[a] = new(pairChunk)
|
||||
copy(self.tail[a][:b+1], con)
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Less(i, j int) bool {
|
||||
return lessFrom(self.At(i).Key, self.At(j).Key, 0)
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Swap(i, j int) {
|
||||
a, b := self.At(i), self.At(j)
|
||||
*a, *b = *b, *a
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Sort() {
|
||||
sort.Sort(self)
|
||||
}
|
||||
|
||||
// Compare two strings from the pos d.
|
||||
func lessFrom(a, b string, d int) bool {
|
||||
l := len(a)
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
for i := d; i < l; i++ {
|
||||
if a[i] == b[i] {
|
||||
continue
|
||||
}
|
||||
return a[i] < b[i]
|
||||
}
|
||||
return len(a) < len(b)
|
||||
}
|
||||
|
||||
type parseObjectStack struct {
|
||||
parser Parser
|
||||
v linkedPairs
|
||||
}
|
||||
|
||||
type parseArrayStack struct {
|
||||
parser Parser
|
||||
v linkedNodes
|
||||
}
|
||||
|
||||
func newLazyArray(p *Parser) Node {
|
||||
s := new(parseArrayStack)
|
||||
s.parser = *p
|
||||
return Node{
|
||||
t: _V_ARRAY_LAZY,
|
||||
p: unsafe.Pointer(s),
|
||||
}
|
||||
}
|
||||
|
||||
func newLazyObject(p *Parser) Node {
|
||||
s := new(parseObjectStack)
|
||||
s.parser = *p
|
||||
return Node{
|
||||
t: _V_OBJECT_LAZY,
|
||||
p: unsafe.Pointer(s),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) getParserAndArrayStack() (*Parser, *parseArrayStack) {
|
||||
stack := (*parseArrayStack)(self.p)
|
||||
return &stack.parser, stack
|
||||
}
|
||||
|
||||
func (self *Node) getParserAndObjectStack() (*Parser, *parseObjectStack) {
|
||||
stack := (*parseObjectStack)(self.p)
|
||||
return &stack.parser, stack
|
||||
}
|
||||
|
@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ast
|
||||
|
||||
// Algorithm 3-way Radix Quicksort, d means the radix.
|
||||
// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html
|
||||
func radixQsort(kvs PairSlice, d, maxDepth int) {
|
||||
for len(kvs) > 11 {
|
||||
// To avoid the worst case of quickSort (time: O(n^2)), use introsort here.
|
||||
// Reference: https://en.wikipedia.org/wiki/Introsort and
|
||||
// https://github.com/golang/go/issues/467
|
||||
if maxDepth == 0 {
|
||||
heapSort(kvs, 0, len(kvs))
|
||||
return
|
||||
}
|
||||
maxDepth--
|
||||
|
||||
p := pivot(kvs, d)
|
||||
lt, i, gt := 0, 0, len(kvs)
|
||||
for i < gt {
|
||||
c := byteAt(kvs[i].Key, d)
|
||||
if c < p {
|
||||
swap(kvs, lt, i)
|
||||
i++
|
||||
lt++
|
||||
} else if c > p {
|
||||
gt--
|
||||
swap(kvs, i, gt)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// kvs[0:lt] < v = kvs[lt:gt] < kvs[gt:len(kvs)]
|
||||
// Native implemention:
|
||||
// radixQsort(kvs[:lt], d, maxDepth)
|
||||
// if p > -1 {
|
||||
// radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
// }
|
||||
// radixQsort(kvs[gt:], d, maxDepth)
|
||||
// Optimize as follows: make recursive calls only for the smaller parts.
|
||||
// Reference: https://www.geeksforgeeks.org/quicksort-tail-call-optimization-reducing-worst-case-space-log-n/
|
||||
if p == -1 {
|
||||
if lt > len(kvs) - gt {
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[:lt]
|
||||
} else {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
kvs = kvs[gt:]
|
||||
}
|
||||
} else {
|
||||
ml := maxThree(lt, gt-lt, len(kvs)-gt)
|
||||
if ml == lt {
|
||||
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[:lt]
|
||||
} else if ml == gt-lt {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[lt:gt]
|
||||
d += 1
|
||||
} else {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
kvs = kvs[gt:]
|
||||
}
|
||||
}
|
||||
}
|
||||
insertRadixSort(kvs, d)
|
||||
}
|
||||
|
||||
func insertRadixSort(kvs PairSlice, d int) {
|
||||
for i := 1; i < len(kvs); i++ {
|
||||
for j := i; j > 0 && lessFrom(kvs[j].Key, kvs[j-1].Key, d); j-- {
|
||||
swap(kvs, j, j-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pivot(kvs PairSlice, d int) int {
|
||||
m := len(kvs) >> 1
|
||||
if len(kvs) > 40 {
|
||||
// Tukey's ``Ninther,'' median of three mediankvs of three.
|
||||
t := len(kvs) / 8
|
||||
return medianThree(
|
||||
medianThree(byteAt(kvs[0].Key, d), byteAt(kvs[t].Key, d), byteAt(kvs[2*t].Key, d)),
|
||||
medianThree(byteAt(kvs[m].Key, d), byteAt(kvs[m-t].Key, d), byteAt(kvs[m+t].Key, d)),
|
||||
medianThree(byteAt(kvs[len(kvs)-1].Key, d),
|
||||
byteAt(kvs[len(kvs)-1-t].Key, d),
|
||||
byteAt(kvs[len(kvs)-1-2*t].Key, d)))
|
||||
}
|
||||
return medianThree(byteAt(kvs[0].Key, d), byteAt(kvs[m].Key, d), byteAt(kvs[len(kvs)-1].Key, d))
|
||||
}
|
||||
|
||||
func medianThree(i, j, k int) int {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
} // i < j
|
||||
if k < i {
|
||||
return i
|
||||
}
|
||||
if k > j {
|
||||
return j
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func maxThree(i, j, k int) int {
|
||||
max := i
|
||||
if max < j {
|
||||
max = j
|
||||
}
|
||||
if max < k {
|
||||
max = k
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// maxDepth returns a threshold at which quicksort should switch
|
||||
// to heapsort. It returnkvs 2*ceil(lg(n+1)).
|
||||
func maxDepth(n int) int {
|
||||
var depth int
|
||||
for i := n; i > 0; i >>= 1 {
|
||||
depth++
|
||||
}
|
||||
return depth * 2
|
||||
}
|
||||
|
||||
// siftDown implements the heap property on kvs[lo:hi].
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDown(kvs PairSlice, lo, hi, first int) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && kvs[first+child].Key < kvs[first+child+1].Key {
|
||||
child++
|
||||
}
|
||||
if kvs[first+root].Key >= kvs[first+child].Key {
|
||||
return
|
||||
}
|
||||
swap(kvs, first+root, first+child)
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func heapSort(kvs PairSlice, a, b int) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with the greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDown(kvs, i, hi, first)
|
||||
}
|
||||
|
||||
// Pop elements, the largest first, into end of kvs.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
swap(kvs, first, first+i)
|
||||
siftDown(kvs, lo, i, first)
|
||||
}
|
||||
}
|
||||
|
||||
// Note that Pair.Key is NOT pointed to Pair.m when map key is integer after swap
|
||||
func swap(kvs PairSlice, a, b int) {
|
||||
kvs[a].Key, kvs[b].Key = kvs[b].Key, kvs[a].Key
|
||||
kvs[a].Value, kvs[b].Value = kvs[b].Value, kvs[a].Value
|
||||
}
|
||||
|
||||
// Compare two strings from the pos d.
|
||||
func lessFrom(a, b string, d int) bool {
|
||||
l := len(a)
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
for i := d; i < l; i++ {
|
||||
if a[i] == b[i] {
|
||||
continue
|
||||
}
|
||||
return a[i] < b[i]
|
||||
}
|
||||
return len(a) < len(b)
|
||||
}
|
||||
|
||||
func byteAt(b string, p int) int {
|
||||
if p < len(b) {
|
||||
return int(b[p])
|
||||
}
|
||||
return -1
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
// Visitor handles the callbacks during preorder traversal of a JSON AST.
|
||||
//
|
||||
// According to the JSON RFC8259, a JSON AST can be defined by
|
||||
// the following rules without seperator / whitespace tokens.
|
||||
//
|
||||
// JSON-AST = value
|
||||
// value = false / null / true / object / array / number / string
|
||||
// object = begin-object [ member *( member ) ] end-object
|
||||
// member = string value
|
||||
// array = begin-array [ value *( value ) ] end-array
|
||||
//
|
||||
type Visitor interface {
|
||||
|
||||
// OnNull handles a JSON null value.
|
||||
OnNull() error
|
||||
|
||||
// OnBool handles a JSON true / false value.
|
||||
OnBool(v bool) error
|
||||
|
||||
// OnString handles a JSON string value.
|
||||
OnString(v string) error
|
||||
|
||||
// OnInt64 handles a JSON number value with int64 type.
|
||||
OnInt64(v int64, n json.Number) error
|
||||
|
||||
// OnFloat64 handles a JSON number value with float64 type.
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
|
||||
// OnObjectBegin handles the beginning of a JSON object value with a
|
||||
// suggested capacity that can be used to make your custom object container.
|
||||
//
|
||||
// After this point the visitor will receive a sequence of callbacks like
|
||||
// [string, value, string, value, ......, ObjectEnd].
|
||||
//
|
||||
// Note:
|
||||
// 1. This is a recursive definition which means the value can
|
||||
// also be a JSON object / array described by a sequence of callbacks.
|
||||
// 2. The suggested capacity will be 0 if current object is empty.
|
||||
// 3. Currently sonic use a fixed capacity for non-empty object (keep in
|
||||
// sync with ast.Node) which might not be very suitable. This may be
|
||||
// improved in future version.
|
||||
OnObjectBegin(capacity int) error
|
||||
|
||||
// OnObjectKey handles a JSON object key string in member.
|
||||
OnObjectKey(key string) error
|
||||
|
||||
// OnObjectEnd handles the ending of a JSON object value.
|
||||
OnObjectEnd() error
|
||||
|
||||
// OnArrayBegin handles the beginning of a JSON array value with a
|
||||
// suggested capacity that can be used to make your custom array container.
|
||||
//
|
||||
// After this point the visitor will receive a sequence of callbacks like
|
||||
// [value, value, value, ......, ArrayEnd].
|
||||
//
|
||||
// Note:
|
||||
// 1. This is a recursive definition which means the value can
|
||||
// also be a JSON object / array described by a sequence of callbacks.
|
||||
// 2. The suggested capacity will be 0 if current array is empty.
|
||||
// 3. Currently sonic use a fixed capacity for non-empty array (keep in
|
||||
// sync with ast.Node) which might not be very suitable. This may be
|
||||
// improved in future version.
|
||||
OnArrayBegin(capacity int) error
|
||||
|
||||
// OnArrayEnd handles the ending of a JSON array value.
|
||||
OnArrayEnd() error
|
||||
}
|
||||
|
||||
// VisitorOptions contains all Visitor's options. The default value is an
|
||||
// empty VisitorOptions{}.
|
||||
type VisitorOptions struct {
|
||||
// OnlyNumber indicates parser to directly return number value without
|
||||
// conversion, then the first argument of OnInt64 / OnFloat64 will always
|
||||
// be zero.
|
||||
OnlyNumber bool
|
||||
}
|
||||
|
||||
var defaultVisitorOptions = &VisitorOptions{}
|
||||
|
||||
// Preorder decodes the whole JSON string and callbacks each AST node to visitor
|
||||
// during preorder traversal. Any visitor method with an error returned will
|
||||
// break the traversal and the given error will be directly returned. The opts
|
||||
// argument can be reused after every call.
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error {
|
||||
if opts == nil {
|
||||
opts = defaultVisitorOptions
|
||||
}
|
||||
// process VisitorOptions first to guarantee that all options will be
|
||||
// constant during decoding and make options more readable.
|
||||
var (
|
||||
optDecodeNumber = !opts.OnlyNumber
|
||||
)
|
||||
|
||||
tv := &traverser{
|
||||
parser: Parser{
|
||||
s: str,
|
||||
noLazy: true,
|
||||
skipValue: false,
|
||||
},
|
||||
visitor: visitor,
|
||||
}
|
||||
|
||||
if optDecodeNumber {
|
||||
tv.parser.decodeNumber(true)
|
||||
}
|
||||
|
||||
err := tv.decodeValue()
|
||||
|
||||
if optDecodeNumber {
|
||||
tv.parser.decodeNumber(false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type traverser struct {
|
||||
parser Parser
|
||||
visitor Visitor
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).Parse method.
|
||||
func (self *traverser) decodeValue() error {
|
||||
switch val := self.parser.decodeValue(); val.Vt {
|
||||
case types.V_EOF:
|
||||
return types.ERR_EOF
|
||||
case types.V_NULL:
|
||||
return self.visitor.OnNull()
|
||||
case types.V_TRUE:
|
||||
return self.visitor.OnBool(true)
|
||||
case types.V_FALSE:
|
||||
return self.visitor.OnBool(false)
|
||||
case types.V_STRING:
|
||||
return self.decodeString(val.Iv, val.Ep)
|
||||
case types.V_DOUBLE:
|
||||
return self.visitor.OnFloat64(val.Dv,
|
||||
json.Number(self.parser.s[val.Ep:self.parser.p]))
|
||||
case types.V_INTEGER:
|
||||
return self.visitor.OnInt64(val.Iv,
|
||||
json.Number(self.parser.s[val.Ep:self.parser.p]))
|
||||
case types.V_ARRAY:
|
||||
return self.decodeArray()
|
||||
case types.V_OBJECT:
|
||||
return self.decodeObject()
|
||||
default:
|
||||
return types.ParsingError(-val.Vt)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeArray method.
|
||||
func (self *traverser) decodeArray() error {
|
||||
sp := self.parser.p
|
||||
ns := len(self.parser.s)
|
||||
|
||||
/* check for EOF */
|
||||
self.parser.p = self.parser.lspace(sp)
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty array */
|
||||
if self.parser.s[self.parser.p] == ']' {
|
||||
self.parser.p++
|
||||
if err := self.visitor.OnArrayBegin(0); err != nil {
|
||||
return err
|
||||
}
|
||||
return self.visitor.OnArrayEnd()
|
||||
}
|
||||
|
||||
/* allocate array space and parse every element */
|
||||
if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
/* decode the value */
|
||||
if err := self.decodeValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
self.parser.p = self.parser.lspace(self.parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.parser.s[self.parser.p] {
|
||||
case ',':
|
||||
self.parser.p++
|
||||
case ']':
|
||||
self.parser.p++
|
||||
return self.visitor.OnArrayEnd()
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeObject method.
|
||||
func (self *traverser) decodeObject() error {
|
||||
sp := self.parser.p
|
||||
ns := len(self.parser.s)
|
||||
|
||||
/* check for EOF */
|
||||
self.parser.p = self.parser.lspace(sp)
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty object */
|
||||
if self.parser.s[self.parser.p] == '}' {
|
||||
self.parser.p++
|
||||
if err := self.visitor.OnObjectBegin(0); err != nil {
|
||||
return err
|
||||
}
|
||||
return self.visitor.OnObjectEnd()
|
||||
}
|
||||
|
||||
/* allocate object space and decode each pair */
|
||||
if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
var njs types.JsonState
|
||||
var err types.ParsingError
|
||||
|
||||
/* decode the key */
|
||||
if njs = self.parser.decodeValue(); njs.Vt != types.V_STRING {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* extract the key */
|
||||
idx := self.parser.p - 1
|
||||
key := self.parser.s[njs.Iv:idx]
|
||||
|
||||
/* check for escape sequence */
|
||||
if njs.Ep != -1 {
|
||||
if key, err = unquote(key); err != 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.visitor.OnObjectKey(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/* expect a ':' delimiter */
|
||||
if err = self.parser.delim(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* decode the value */
|
||||
if err := self.decodeValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.parser.p = self.parser.lspace(self.parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.parser.s[self.parser.p] {
|
||||
case ',':
|
||||
self.parser.p++
|
||||
case '}':
|
||||
self.parser.p++
|
||||
return self.visitor.OnObjectEnd()
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeString method.
|
||||
func (self *traverser) decodeString(iv int64, ep int) error {
|
||||
p := self.parser.p - 1
|
||||
s := self.parser.s[iv:p]
|
||||
|
||||
/* fast path: no escape sequence */
|
||||
if ep == -1 {
|
||||
return self.visitor.OnString(s)
|
||||
}
|
||||
|
||||
/* unquote the string */
|
||||
out, err := unquote(s)
|
||||
if err != 0 {
|
||||
return err
|
||||
}
|
||||
return self.visitor.OnString(out)
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`reflect`
|
||||
`sort`
|
||||
`strings`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
type FunctionLayout struct {
|
||||
FP uint32
|
||||
Args []Parameter
|
||||
Rets []Parameter
|
||||
}
|
||||
|
||||
func (self FunctionLayout) String() string {
|
||||
return self.formatFn()
|
||||
}
|
||||
|
||||
func (self FunctionLayout) ArgSize() uint32 {
|
||||
size := uintptr(0)
|
||||
for _, arg := range self.Args {
|
||||
size += arg.Type.Size()
|
||||
}
|
||||
return uint32(size)
|
||||
}
|
||||
|
||||
type slot struct {
|
||||
p bool
|
||||
m uint32
|
||||
}
|
||||
|
||||
func (self FunctionLayout) StackMap() *rt.StackMap {
|
||||
var st []slot
|
||||
var mb rt.StackMapBuilder
|
||||
|
||||
/* add arguments */
|
||||
for _, v := range self.Args {
|
||||
st = append(st, slot {
|
||||
m: v.Mem,
|
||||
p: v.IsPointer,
|
||||
})
|
||||
}
|
||||
|
||||
/* add stack-passed return values */
|
||||
for _, v := range self.Rets {
|
||||
if !v.InRegister {
|
||||
st = append(st, slot {
|
||||
m: v.Mem,
|
||||
p: v.IsPointer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* sort by memory offset */
|
||||
sort.Slice(st, func(i int, j int) bool {
|
||||
return st[i].m < st[j].m
|
||||
})
|
||||
|
||||
/* add the bits */
|
||||
for _, v := range st {
|
||||
mb.AddField(v.p)
|
||||
}
|
||||
|
||||
/* build the stack map */
|
||||
return mb.Build()
|
||||
}
|
||||
|
||||
func (self FunctionLayout) formatFn() string {
|
||||
fp := self.FP
|
||||
return fmt.Sprintf("\n%#04x\nRets:\n%s\nArgs:\n%s", fp, self.formatSeq(self.Rets, &fp), self.formatSeq(self.Args, &fp))
|
||||
}
|
||||
|
||||
func (self FunctionLayout) formatSeq(v []Parameter, fp *uint32) string {
|
||||
nb := len(v)
|
||||
mm := make([]string, 0, len(v))
|
||||
|
||||
/* convert each part */
|
||||
for i := nb-1; i >=0; i-- {
|
||||
*fp -= PtrSize
|
||||
mm = append(mm, fmt.Sprintf("%#04x %s", *fp, v[i].String()))
|
||||
}
|
||||
|
||||
/* join them together */
|
||||
return strings.Join(mm, "\n")
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
desc *FunctionLayout
|
||||
locals []bool
|
||||
ccall bool
|
||||
}
|
||||
|
||||
func NewFrame(desc *FunctionLayout, locals []bool, ccall bool) Frame {
|
||||
fr := Frame{}
|
||||
fr.desc = desc
|
||||
fr.locals = locals
|
||||
fr.ccall = ccall
|
||||
return fr
|
||||
}
|
||||
|
||||
func (self *Frame) String() string {
|
||||
out := self.desc.String()
|
||||
|
||||
off := -8
|
||||
out += fmt.Sprintf("\n%#4x [Return PC]", off)
|
||||
off -= 8
|
||||
out += fmt.Sprintf("\n%#4x [RBP]", off)
|
||||
off -= 8
|
||||
|
||||
for _, v := range ReservedRegs(self.ccall) {
|
||||
out += fmt.Sprintf("\n%#4x [%v]", off, v)
|
||||
off -= PtrSize
|
||||
}
|
||||
|
||||
for _, b := range self.locals {
|
||||
out += fmt.Sprintf("\n%#4x [%v]", off, b)
|
||||
off -= PtrSize
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (self *Frame) Prev() uint32 {
|
||||
return self.Size() + PtrSize
|
||||
}
|
||||
|
||||
func (self *Frame) Size() uint32 {
|
||||
return uint32(self.Offs() + PtrSize)
|
||||
}
|
||||
|
||||
func (self *Frame) Offs() uint32 {
|
||||
return uint32(len(ReservedRegs(self.ccall)) * PtrSize + len(self.locals)*PtrSize)
|
||||
}
|
||||
|
||||
func (self *Frame) ArgPtrs() *rt.StackMap {
|
||||
return self.desc.StackMap()
|
||||
}
|
||||
|
||||
func (self *Frame) LocalPtrs() *rt.StackMap {
|
||||
var m rt.StackMapBuilder
|
||||
for _, b := range self.locals {
|
||||
m.AddFields(len(ReservedRegs(self.ccall)), b)
|
||||
}
|
||||
return m.Build()
|
||||
}
|
||||
|
||||
func alignUp(n uint32, a int) uint32 {
|
||||
return (uint32(n) + uint32(a) - 1) &^ (uint32(a) - 1)
|
||||
}
|
||||
|
||||
func isPointer(vt reflect.Type) bool {
|
||||
switch vt.Kind() {
|
||||
case reflect.Bool : fallthrough
|
||||
case reflect.Int : fallthrough
|
||||
case reflect.Int8 : fallthrough
|
||||
case reflect.Int16 : fallthrough
|
||||
case reflect.Int32 : fallthrough
|
||||
case reflect.Int64 : fallthrough
|
||||
case reflect.Uint : fallthrough
|
||||
case reflect.Uint8 : fallthrough
|
||||
case reflect.Uint16 : fallthrough
|
||||
case reflect.Uint32 : fallthrough
|
||||
case reflect.Uint64 : fallthrough
|
||||
case reflect.Float32 : fallthrough
|
||||
case reflect.Float64 : fallthrough
|
||||
case reflect.Uintptr : return false
|
||||
case reflect.Chan : fallthrough
|
||||
case reflect.Func : fallthrough
|
||||
case reflect.Map : fallthrough
|
||||
case reflect.Ptr : fallthrough
|
||||
case reflect.UnsafePointer : return true
|
||||
case reflect.Complex64 : fallthrough
|
||||
case reflect.Complex128 : fallthrough
|
||||
case reflect.Array : fallthrough
|
||||
case reflect.Struct : panic("abi: unsupported types")
|
||||
default : panic("abi: invalid value type")
|
||||
}
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`reflect`
|
||||
`unsafe`
|
||||
|
||||
. `github.com/chenzhuoyu/iasm/x86_64`
|
||||
)
|
||||
|
||||
const (
|
||||
PtrSize = 8 // pointer size
|
||||
PtrAlign = 8 // pointer alignment
|
||||
)
|
||||
|
||||
var iregOrderC = []Register{
|
||||
RDI,
|
||||
RSI,
|
||||
RDX,
|
||||
RCX,
|
||||
R8,
|
||||
R9,
|
||||
}
|
||||
|
||||
var xregOrderC = []Register{
|
||||
XMM0,
|
||||
XMM1,
|
||||
XMM2,
|
||||
XMM3,
|
||||
XMM4,
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
}
|
||||
|
||||
var (
|
||||
intType = reflect.TypeOf(0)
|
||||
ptrType = reflect.TypeOf(unsafe.Pointer(nil))
|
||||
)
|
||||
|
||||
func (self *Frame) argv(i int) *MemoryOperand {
|
||||
return Ptr(RSP, int32(self.Prev() + self.desc.Args[i].Mem))
|
||||
}
|
||||
|
||||
// spillv is used for growstack spill registers
|
||||
func (self *Frame) spillv(i int) *MemoryOperand {
|
||||
// remain one slot for caller return pc
|
||||
return Ptr(RSP, PtrSize + int32(self.desc.Args[i].Mem))
|
||||
}
|
||||
|
||||
func (self *Frame) retv(i int) *MemoryOperand {
|
||||
return Ptr(RSP, int32(self.Prev() + self.desc.Rets[i].Mem))
|
||||
}
|
||||
|
||||
func (self *Frame) resv(i int) *MemoryOperand {
|
||||
return Ptr(RSP, int32(self.Offs() - uint32((i+1) * PtrSize)))
|
||||
}
|
||||
|
||||
func (self *Frame) emitGrowStack(p *Program, entry *Label) {
|
||||
// spill all register arguments
|
||||
for i, v := range self.desc.Args {
|
||||
if v.InRegister {
|
||||
if v.IsFloat == floatKind64 {
|
||||
p.MOVSD(v.Reg, self.spillv(i))
|
||||
} else if v.IsFloat == floatKind32 {
|
||||
p.MOVSS(v.Reg, self.spillv(i))
|
||||
}else {
|
||||
p.MOVQ(v.Reg, self.spillv(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call runtime.morestack_noctxt
|
||||
p.MOVQ(F_morestack_noctxt, R12)
|
||||
p.CALLQ(R12)
|
||||
// load all register arguments
|
||||
for i, v := range self.desc.Args {
|
||||
if v.InRegister {
|
||||
if v.IsFloat == floatKind64 {
|
||||
p.MOVSD(self.spillv(i), v.Reg)
|
||||
} else if v.IsFloat == floatKind32 {
|
||||
p.MOVSS(self.spillv(i), v.Reg)
|
||||
}else {
|
||||
p.MOVQ(self.spillv(i), v.Reg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// jump back to the function entry
|
||||
p.JMP(entry)
|
||||
}
|
||||
|
||||
func (self *Frame) GrowStackTextSize() uint32 {
|
||||
p := DefaultArch.CreateProgram()
|
||||
// spill all register arguments
|
||||
for i, v := range self.desc.Args {
|
||||
if v.InRegister {
|
||||
if v.IsFloat == floatKind64 {
|
||||
p.MOVSD(v.Reg, self.spillv(i))
|
||||
} else if v.IsFloat == floatKind32 {
|
||||
p.MOVSS(v.Reg, self.spillv(i))
|
||||
}else {
|
||||
p.MOVQ(v.Reg, self.spillv(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call runtime.morestack_noctxt
|
||||
p.MOVQ(F_morestack_noctxt, R12)
|
||||
p.CALLQ(R12)
|
||||
// load all register arguments
|
||||
for i, v := range self.desc.Args {
|
||||
if v.InRegister {
|
||||
if v.IsFloat == floatKind64 {
|
||||
p.MOVSD(self.spillv(i), v.Reg)
|
||||
} else if v.IsFloat == floatKind32 {
|
||||
p.MOVSS(self.spillv(i), v.Reg)
|
||||
} else {
|
||||
p.MOVQ(self.spillv(i), v.Reg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// jump back to the function entry
|
||||
l := CreateLabel("")
|
||||
p.Link(l)
|
||||
p.JMP(l)
|
||||
|
||||
return uint32(len(p.Assemble(0)))
|
||||
}
|
||||
|
||||
func (self *Frame) emitPrologue(p *Program) {
|
||||
p.SUBQ(self.Size(), RSP)
|
||||
p.MOVQ(RBP, Ptr(RSP, int32(self.Offs())))
|
||||
p.LEAQ(Ptr(RSP, int32(self.Offs())), RBP)
|
||||
}
|
||||
|
||||
func (self *Frame) emitEpilogue(p *Program) {
|
||||
p.MOVQ(Ptr(RSP, int32(self.Offs())), RBP)
|
||||
p.ADDQ(self.Size(), RSP)
|
||||
p.RET()
|
||||
}
|
||||
|
||||
func (self *Frame) emitReserveRegs(p *Program) {
|
||||
// spill reserved registers
|
||||
for i, r := range ReservedRegs(self.ccall) {
|
||||
switch r.(type) {
|
||||
case Register64:
|
||||
p.MOVQ(r, self.resv(i))
|
||||
case XMMRegister:
|
||||
p.MOVSD(r, self.resv(i))
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported register type %t to reserve", r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitSpillPtrs(p *Program) {
|
||||
// spill pointer argument registers
|
||||
for i, r := range self.desc.Args {
|
||||
if r.InRegister && r.IsPointer {
|
||||
p.MOVQ(r.Reg, self.argv(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitClearPtrs(p *Program) {
|
||||
// spill pointer argument registers
|
||||
for i, r := range self.desc.Args {
|
||||
if r.InRegister && r.IsPointer {
|
||||
p.MOVQ(int64(0), self.argv(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitCallC(p *Program, addr uintptr) {
|
||||
p.MOVQ(addr, RAX)
|
||||
p.CALLQ(RAX)
|
||||
}
|
||||
|
||||
type floatKind uint8
|
||||
|
||||
const (
|
||||
notFloatKind floatKind = iota
|
||||
floatKind32
|
||||
floatKind64
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
InRegister bool
|
||||
IsPointer bool
|
||||
IsFloat floatKind
|
||||
Reg Register
|
||||
Mem uint32
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func mkIReg(vt reflect.Type, reg Register64) (p Parameter) {
|
||||
p.Reg = reg
|
||||
p.Type = vt
|
||||
p.InRegister = true
|
||||
p.IsPointer = isPointer(vt)
|
||||
return
|
||||
}
|
||||
|
||||
func isFloat(vt reflect.Type) floatKind {
|
||||
switch vt.Kind() {
|
||||
case reflect.Float32:
|
||||
return floatKind32
|
||||
case reflect.Float64:
|
||||
return floatKind64
|
||||
default:
|
||||
return notFloatKind
|
||||
}
|
||||
}
|
||||
|
||||
func mkXReg(vt reflect.Type, reg XMMRegister) (p Parameter) {
|
||||
p.Reg = reg
|
||||
p.Type = vt
|
||||
p.InRegister = true
|
||||
p.IsFloat = isFloat(vt)
|
||||
return
|
||||
}
|
||||
|
||||
func mkStack(vt reflect.Type, mem uint32) (p Parameter) {
|
||||
p.Mem = mem
|
||||
p.Type = vt
|
||||
p.InRegister = false
|
||||
p.IsPointer = isPointer(vt)
|
||||
p.IsFloat = isFloat(vt)
|
||||
return
|
||||
}
|
||||
|
||||
func (self Parameter) String() string {
|
||||
if self.InRegister {
|
||||
return fmt.Sprintf("[%%%s, Pointer(%v), Float(%v)]", self.Reg, self.IsPointer, self.IsFloat)
|
||||
} else {
|
||||
return fmt.Sprintf("[%d(FP), Pointer(%v), Float(%v)]", self.Mem, self.IsPointer, self.IsFloat)
|
||||
}
|
||||
}
|
||||
|
||||
func CallC(addr uintptr, fr Frame, maxStack uintptr) []byte {
|
||||
p := DefaultArch.CreateProgram()
|
||||
|
||||
stack := CreateLabel("_stack_grow")
|
||||
entry := CreateLabel("_entry")
|
||||
p.Link(entry)
|
||||
fr.emitStackCheck(p, stack, maxStack)
|
||||
fr.emitPrologue(p)
|
||||
fr.emitReserveRegs(p)
|
||||
fr.emitSpillPtrs(p)
|
||||
fr.emitExchangeArgs(p)
|
||||
fr.emitCallC(p, addr)
|
||||
fr.emitExchangeRets(p)
|
||||
fr.emitRestoreRegs(p)
|
||||
fr.emitEpilogue(p)
|
||||
p.Link(stack)
|
||||
fr.emitGrowStack(p, entry)
|
||||
|
||||
return p.Assemble(0)
|
||||
}
|
||||
|
||||
|
||||
func (self *Frame) emitDebug(p *Program) {
|
||||
p.INT(3)
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
/*
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`reflect`
|
||||
`runtime`
|
||||
|
||||
. `github.com/chenzhuoyu/iasm/x86_64`
|
||||
)
|
||||
|
||||
func ReservedRegs(callc bool) []Register {
|
||||
return nil
|
||||
}
|
||||
|
||||
func salloc(p []Parameter, sp uint32, vt reflect.Type) (uint32, []Parameter) {
|
||||
switch vt.Kind() {
|
||||
case reflect.Bool : return sp + 8, append(p, mkStack(reflect.TypeOf(false), sp))
|
||||
case reflect.Int : return sp + 8, append(p, mkStack(intType, sp))
|
||||
case reflect.Int8 : return sp + 8, append(p, mkStack(reflect.TypeOf(int8(0)), sp))
|
||||
case reflect.Int16 : return sp + 8, append(p, mkStack(reflect.TypeOf(int16(0)), sp))
|
||||
case reflect.Int32 : return sp + 8, append(p, mkStack(reflect.TypeOf(int32(0)), sp))
|
||||
case reflect.Int64 : return sp + 8, append(p, mkStack(reflect.TypeOf(int64(0)), sp))
|
||||
case reflect.Uint : return sp + 8, append(p, mkStack(reflect.TypeOf(uint(0)), sp))
|
||||
case reflect.Uint8 : return sp + 8, append(p, mkStack(reflect.TypeOf(uint8(0)), sp))
|
||||
case reflect.Uint16 : return sp + 8, append(p, mkStack(reflect.TypeOf(uint16(0)), sp))
|
||||
case reflect.Uint32 : return sp + 8, append(p, mkStack(reflect.TypeOf(uint32(0)), sp))
|
||||
case reflect.Uint64 : return sp + 8, append(p, mkStack(reflect.TypeOf(uint64(0)), sp))
|
||||
case reflect.Uintptr : return sp + 8, append(p, mkStack(reflect.TypeOf(uintptr(0)), sp))
|
||||
case reflect.Float32 : return sp + 8, append(p, mkStack(reflect.TypeOf(float32(0)), sp))
|
||||
case reflect.Float64 : return sp + 8, append(p, mkStack(reflect.TypeOf(float64(0)), sp))
|
||||
case reflect.Complex64 : panic("abi: go116: not implemented: complex64")
|
||||
case reflect.Complex128 : panic("abi: go116: not implemented: complex128")
|
||||
case reflect.Array : panic("abi: go116: not implemented: arrays")
|
||||
case reflect.Chan : return sp + 8, append(p, mkStack(reflect.TypeOf((chan int)(nil)), sp))
|
||||
case reflect.Func : return sp + 8, append(p, mkStack(reflect.TypeOf((func())(nil)), sp))
|
||||
case reflect.Map : return sp + 8, append(p, mkStack(reflect.TypeOf((map[int]int)(nil)), sp))
|
||||
case reflect.Ptr : return sp + 8, append(p, mkStack(reflect.TypeOf((*int)(nil)), sp))
|
||||
case reflect.UnsafePointer : return sp + 8, append(p, mkStack(ptrType, sp))
|
||||
case reflect.Interface : return sp + 16, append(p, mkStack(ptrType, sp), mkStack(ptrType, sp + 8))
|
||||
case reflect.Slice : return sp + 24, append(p, mkStack(ptrType, sp), mkStack(intType, sp + 8), mkStack(intType, sp + 16))
|
||||
case reflect.String : return sp + 16, append(p, mkStack(ptrType, sp), mkStack(intType, sp + 8))
|
||||
case reflect.Struct : panic("abi: go116: not implemented: structs")
|
||||
default : panic("abi: invalid value type")
|
||||
}
|
||||
}
|
||||
|
||||
func NewFunctionLayout(ft reflect.Type) FunctionLayout {
|
||||
var sp uint32
|
||||
var fn FunctionLayout
|
||||
|
||||
/* assign every arguments */
|
||||
for i := 0; i < ft.NumIn(); i++ {
|
||||
sp, fn.Args = salloc(fn.Args, sp, ft.In(i))
|
||||
}
|
||||
|
||||
/* assign every return value */
|
||||
for i := 0; i < ft.NumOut(); i++ {
|
||||
sp, fn.Rets = salloc(fn.Rets, sp, ft.Out(i))
|
||||
}
|
||||
|
||||
/* update function ID and stack pointer */
|
||||
fn.FP = sp
|
||||
return fn
|
||||
}
|
||||
|
||||
func (self *Frame) emitExchangeArgs(p *Program) {
|
||||
iregArgs, xregArgs := 0, 0
|
||||
for _, v := range self.desc.Args {
|
||||
if v.IsFloat != notFloatKind {
|
||||
xregArgs += 1
|
||||
} else {
|
||||
iregArgs += 1
|
||||
}
|
||||
}
|
||||
|
||||
if iregArgs > len(iregOrderC) {
|
||||
panic("too many arguments, only support at most 6 integer arguments now")
|
||||
}
|
||||
if xregArgs > len(xregOrderC) {
|
||||
panic("too many arguments, only support at most 8 float arguments now")
|
||||
}
|
||||
|
||||
ic, xc := iregArgs, xregArgs
|
||||
for i := 0; i < len(self.desc.Args); i++ {
|
||||
arg := self.desc.Args[i]
|
||||
if arg.IsFloat == floatKind64 {
|
||||
p.MOVSD(self.argv(i), xregOrderC[xregArgs - xc])
|
||||
xc -= 1
|
||||
} else if arg.IsFloat == floatKind32 {
|
||||
p.MOVSS(self.argv(i), xregOrderC[xregArgs - xc])
|
||||
xc -= 1
|
||||
} else {
|
||||
p.MOVQ(self.argv(i), iregOrderC[iregArgs - ic])
|
||||
ic -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitStackCheck(p *Program, to *Label, maxStack uintptr) {
|
||||
// get the current goroutine
|
||||
switch runtime.GOOS {
|
||||
case "linux" : p.MOVQ(Abs(-8), R14).FS()
|
||||
case "darwin" : p.MOVQ(Abs(0x30), R14).GS()
|
||||
case "windows": break // windows always stores G pointer at R14
|
||||
default : panic("unsupported operating system")
|
||||
}
|
||||
|
||||
// check the stack guard
|
||||
p.LEAQ(Ptr(RSP, -int32(self.Size() + uint32(maxStack))), RAX)
|
||||
p.CMPQ(Ptr(R14, _G_stackguard0), RAX)
|
||||
p.JBE(to)
|
||||
}
|
||||
|
||||
func (self *Frame) StackCheckTextSize() uint32 {
|
||||
p := DefaultArch.CreateProgram()
|
||||
|
||||
// get the current goroutine
|
||||
switch runtime.GOOS {
|
||||
case "linux" : p.MOVQ(Abs(-8), R14).FS()
|
||||
case "darwin" : p.MOVQ(Abs(0x30), R14).GS()
|
||||
case "windows": break // windows always stores G pointer at R14
|
||||
default : panic("unsupported operating system")
|
||||
}
|
||||
|
||||
// check the stack guard
|
||||
p.LEAQ(Ptr(RSP, -int32(self.Size())), RAX)
|
||||
p.CMPQ(Ptr(R14, _G_stackguard0), RAX)
|
||||
l := CreateLabel("")
|
||||
p.Link(l)
|
||||
p.JBE(l)
|
||||
|
||||
return uint32(len(p.Assemble(0)))
|
||||
}
|
||||
|
||||
func (self *Frame) emitExchangeRets(p *Program) {
|
||||
if len(self.desc.Rets) > 1 {
|
||||
panic("too many results, only support one result now")
|
||||
}
|
||||
// store result
|
||||
if len(self.desc.Rets) ==1 {
|
||||
if self.desc.Rets[0].IsFloat == floatKind64 {
|
||||
p.MOVSD(xregOrderC[0], self.retv(0))
|
||||
} else if self.desc.Rets[0].IsFloat == floatKind32 {
|
||||
p.MOVSS(xregOrderC[0], self.retv(0))
|
||||
} else {
|
||||
p.MOVQ(RAX, self.retv(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitRestoreRegs(p *Program) {
|
||||
// load reserved registers
|
||||
for i, r := range ReservedRegs(self.ccall) {
|
||||
switch r.(type) {
|
||||
case Register64:
|
||||
p.MOVQ(self.resv(i), r)
|
||||
case XMMRegister:
|
||||
p.MOVSD(self.resv(i), r)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported register type %t to reserve", r))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,316 @@
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
/*
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** Go Internal ABI implementation
|
||||
*
|
||||
* This module implements the function layout algorithm described by the Go internal ABI.
|
||||
* See https://github.com/golang/go/blob/master/src/cmd/compile/abi-internal.md for more info.
|
||||
*/
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`reflect`
|
||||
|
||||
. `github.com/chenzhuoyu/iasm/x86_64`
|
||||
)
|
||||
|
||||
/** Frame Structure of the Generated Function
|
||||
FP +------------------------------+
|
||||
| . . . |
|
||||
| 2nd reg argument spill space |
|
||||
+ 1st reg argument spill space |
|
||||
| <pointer-sized alignment> |
|
||||
| . . . |
|
||||
| 2nd stack-assigned result |
|
||||
+ 1st stack-assigned result |
|
||||
| <pointer-sized alignment> |
|
||||
| . . . |
|
||||
| 2nd stack-assigned argument |
|
||||
| 1st stack-assigned argument |
|
||||
| stack-assigned receiver |
|
||||
prev() +------------------------------+ (Previous Frame)
|
||||
Return PC |
|
||||
size() -------------------------------|
|
||||
Saved RBP |
|
||||
offs() -------------------------------|
|
||||
1th Reserved Registers |
|
||||
-------------------------------|
|
||||
2th Reserved Registers |
|
||||
-------------------------------|
|
||||
Local Variables |
|
||||
RSP -------------------------------|↓ lower addresses
|
||||
*/
|
||||
|
||||
const zeroRegGo = XMM15
|
||||
|
||||
var iregOrderGo = [...]Register64 {
|
||||
RAX,// RDI
|
||||
RBX,// RSI
|
||||
RCX,// RDX
|
||||
RDI,// RCX
|
||||
RSI,// R8
|
||||
R8, // R9
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
}
|
||||
|
||||
var xregOrderGo = [...]XMMRegister {
|
||||
XMM0,
|
||||
XMM1,
|
||||
XMM2,
|
||||
XMM3,
|
||||
XMM4,
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
XMM8,
|
||||
XMM9,
|
||||
XMM10,
|
||||
XMM11,
|
||||
XMM12,
|
||||
XMM13,
|
||||
XMM14,
|
||||
}
|
||||
|
||||
func ReservedRegs(callc bool) []Register {
|
||||
if callc {
|
||||
return nil
|
||||
}
|
||||
return []Register {
|
||||
R14, // current goroutine
|
||||
R15, // GOT reference
|
||||
}
|
||||
}
|
||||
|
||||
type stackAlloc struct {
|
||||
s uint32
|
||||
i int
|
||||
x int
|
||||
}
|
||||
|
||||
func (self *stackAlloc) reset() {
|
||||
self.i, self.x = 0, 0
|
||||
}
|
||||
|
||||
func (self *stackAlloc) ireg(vt reflect.Type) (p Parameter) {
|
||||
p = mkIReg(vt, iregOrderGo[self.i])
|
||||
self.i++
|
||||
return
|
||||
}
|
||||
|
||||
func (self *stackAlloc) xreg(vt reflect.Type) (p Parameter) {
|
||||
p = mkXReg(vt, xregOrderGo[self.x])
|
||||
self.x++
|
||||
return
|
||||
}
|
||||
|
||||
func (self *stackAlloc) stack(vt reflect.Type) (p Parameter) {
|
||||
p = mkStack(vt, self.s)
|
||||
self.s += uint32(vt.Size())
|
||||
return
|
||||
}
|
||||
|
||||
func (self *stackAlloc) spill(n uint32, a int) uint32 {
|
||||
self.s = alignUp(self.s, a) + n
|
||||
return self.s
|
||||
}
|
||||
|
||||
func (self *stackAlloc) alloc(p []Parameter, vt reflect.Type) []Parameter {
|
||||
nb := vt.Size()
|
||||
vk := vt.Kind()
|
||||
|
||||
/* zero-sized objects are allocated on stack */
|
||||
if nb == 0 {
|
||||
return append(p, mkStack(intType, self.s))
|
||||
}
|
||||
|
||||
/* check for value type */
|
||||
switch vk {
|
||||
case reflect.Bool : return self.valloc(p, reflect.TypeOf(false))
|
||||
case reflect.Int : return self.valloc(p, intType)
|
||||
case reflect.Int8 : return self.valloc(p, reflect.TypeOf(int8(0)))
|
||||
case reflect.Int16 : return self.valloc(p, reflect.TypeOf(int16(0)))
|
||||
case reflect.Int32 : return self.valloc(p, reflect.TypeOf(uint32(0)))
|
||||
case reflect.Int64 : return self.valloc(p, reflect.TypeOf(int64(0)))
|
||||
case reflect.Uint : return self.valloc(p, reflect.TypeOf(uint(0)))
|
||||
case reflect.Uint8 : return self.valloc(p, reflect.TypeOf(uint8(0)))
|
||||
case reflect.Uint16 : return self.valloc(p, reflect.TypeOf(uint16(0)))
|
||||
case reflect.Uint32 : return self.valloc(p, reflect.TypeOf(uint32(0)))
|
||||
case reflect.Uint64 : return self.valloc(p, reflect.TypeOf(uint64(0)))
|
||||
case reflect.Uintptr : return self.valloc(p, reflect.TypeOf(uintptr(0)))
|
||||
case reflect.Float32 : return self.valloc(p, reflect.TypeOf(float32(0)))
|
||||
case reflect.Float64 : return self.valloc(p, reflect.TypeOf(float64(0)))
|
||||
case reflect.Complex64 : panic("abi: go117: not implemented: complex64")
|
||||
case reflect.Complex128 : panic("abi: go117: not implemented: complex128")
|
||||
case reflect.Array : panic("abi: go117: not implemented: arrays")
|
||||
case reflect.Chan : return self.valloc(p, reflect.TypeOf((chan int)(nil)))
|
||||
case reflect.Func : return self.valloc(p, reflect.TypeOf((func())(nil)))
|
||||
case reflect.Map : return self.valloc(p, reflect.TypeOf((map[int]int)(nil)))
|
||||
case reflect.Ptr : return self.valloc(p, reflect.TypeOf((*int)(nil)))
|
||||
case reflect.UnsafePointer : return self.valloc(p, ptrType)
|
||||
case reflect.Interface : return self.valloc(p, ptrType, ptrType)
|
||||
case reflect.Slice : return self.valloc(p, ptrType, intType, intType)
|
||||
case reflect.String : return self.valloc(p, ptrType, intType)
|
||||
case reflect.Struct : panic("abi: go117: not implemented: structs")
|
||||
default : panic("abi: invalid value type")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *stackAlloc) valloc(p []Parameter, vts ...reflect.Type) []Parameter {
|
||||
for _, vt := range vts {
|
||||
enum := isFloat(vt)
|
||||
if enum != notFloatKind && self.x < len(xregOrderGo) {
|
||||
p = append(p, self.xreg(vt))
|
||||
} else if enum == notFloatKind && self.i < len(iregOrderGo) {
|
||||
p = append(p, self.ireg(vt))
|
||||
} else {
|
||||
p = append(p, self.stack(vt))
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func NewFunctionLayout(ft reflect.Type) FunctionLayout {
|
||||
var sa stackAlloc
|
||||
var fn FunctionLayout
|
||||
|
||||
/* assign every arguments */
|
||||
for i := 0; i < ft.NumIn(); i++ {
|
||||
fn.Args = sa.alloc(fn.Args, ft.In(i))
|
||||
}
|
||||
|
||||
/* reset the register counter, and add a pointer alignment field */
|
||||
sa.reset()
|
||||
|
||||
/* assign every return value */
|
||||
for i := 0; i < ft.NumOut(); i++ {
|
||||
fn.Rets = sa.alloc(fn.Rets, ft.Out(i))
|
||||
}
|
||||
|
||||
sa.spill(0, PtrAlign)
|
||||
|
||||
/* assign spill slots */
|
||||
for i := 0; i < len(fn.Args); i++ {
|
||||
if fn.Args[i].InRegister {
|
||||
fn.Args[i].Mem = sa.spill(PtrSize, PtrAlign) - PtrSize
|
||||
}
|
||||
}
|
||||
|
||||
/* add the final pointer alignment field */
|
||||
fn.FP = sa.spill(0, PtrAlign)
|
||||
return fn
|
||||
}
|
||||
|
||||
func (self *Frame) emitExchangeArgs(p *Program) {
|
||||
iregArgs := make([]Parameter, 0, len(self.desc.Args))
|
||||
xregArgs := 0
|
||||
for _, v := range self.desc.Args {
|
||||
if v.InRegister {
|
||||
if v.IsFloat != notFloatKind {
|
||||
xregArgs += 1
|
||||
} else {
|
||||
iregArgs = append(iregArgs, v)
|
||||
}
|
||||
} else {
|
||||
panic("not support stack-assgined arguments now")
|
||||
}
|
||||
}
|
||||
if xregArgs > len(xregOrderC) {
|
||||
panic("too many arguments, only support at most 8 integer register arguments now")
|
||||
}
|
||||
|
||||
switch len(iregArgs) {
|
||||
case 0, 1, 2, 3: {
|
||||
//Fast-Path: when arguments count are less than four, just exchange the registers
|
||||
for i := 0; i < len(iregArgs); i++ {
|
||||
p.MOVQ(iregOrderGo[i], iregOrderC[i])
|
||||
}
|
||||
}
|
||||
case 4, 5, 6: {
|
||||
// need to spill 3th ~ regArgs registers before exchange
|
||||
for i := 3; i < len(iregArgs); i++ {
|
||||
arg := iregArgs[i]
|
||||
// pointer args have already been spilled
|
||||
if !arg.IsPointer {
|
||||
p.MOVQ(iregOrderGo[i], Ptr(RSP, int32(self.Prev() + arg.Mem)))
|
||||
}
|
||||
}
|
||||
p.MOVQ(iregOrderGo[0], iregOrderC[0])
|
||||
p.MOVQ(iregOrderGo[1], iregOrderC[1])
|
||||
p.MOVQ(iregOrderGo[2], iregOrderC[2])
|
||||
for i := 3; i < len(iregArgs); i++ {
|
||||
arg := iregArgs[i]
|
||||
p.MOVQ(Ptr(RSP, int32(self.Prev() + arg.Mem)), iregOrderC[i])
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("too many arguments, only support at most 6 integer register arguments now")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitStackCheck(p *Program, to *Label, maxStack uintptr) {
|
||||
p.LEAQ(Ptr(RSP, int32(-(self.Size() + uint32(maxStack)))), R12)
|
||||
p.CMPQ(Ptr(R14, _G_stackguard0), R12)
|
||||
p.JBE(to)
|
||||
}
|
||||
|
||||
func (self *Frame) StackCheckTextSize() uint32 {
|
||||
p := DefaultArch.CreateProgram()
|
||||
p.LEAQ(Ptr(RSP, int32(-(self.Size()))), R12)
|
||||
p.CMPQ(Ptr(R14, _G_stackguard0), R12)
|
||||
to := CreateLabel("")
|
||||
p.Link(to)
|
||||
p.JBE(to)
|
||||
return uint32(len(p.Assemble(0)))
|
||||
}
|
||||
|
||||
func (self *Frame) emitExchangeRets(p *Program) {
|
||||
if len(self.desc.Rets) > 1 {
|
||||
panic("too many results, only support one result now")
|
||||
}
|
||||
// store result
|
||||
if len(self.desc.Rets) == 1 && !self.desc.Rets[0].InRegister {
|
||||
if self.desc.Rets[0].IsFloat == floatKind64 {
|
||||
p.MOVSD(xregOrderC[0], self.retv(0))
|
||||
} else if self.desc.Rets[0].IsFloat == floatKind32 {
|
||||
p.MOVSS(xregOrderC[0], self.retv(0))
|
||||
} else {
|
||||
p.MOVQ(RAX, self.retv(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Frame) emitRestoreRegs(p *Program) {
|
||||
// load reserved registers
|
||||
for i, r := range ReservedRegs(self.ccall) {
|
||||
switch r.(type) {
|
||||
case Register64:
|
||||
p.MOVQ(self.resv(i), r)
|
||||
case XMMRegister:
|
||||
p.MOVSD(self.resv(i), r)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported register type %t to reserve", r))
|
||||
}
|
||||
}
|
||||
// zero xmm15 for go abi
|
||||
p.XORPS(zeroRegGo, zeroRegGo)
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
_ `unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
_G_stackguard0 = 0x10
|
||||
)
|
||||
|
||||
var (
|
||||
F_morestack_noctxt = uintptr(rt.FuncAddr(morestack_noctxt))
|
||||
)
|
||||
|
||||
//go:linkname morestack_noctxt runtime.morestack_noctxt
|
||||
func morestack_noctxt()
|
||||
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
`sync`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
//go:linkname lastmoduledatap runtime.lastmoduledatap
|
||||
//goland:noinspection GoUnusedGlobalVariable
|
||||
var lastmoduledatap *_ModuleData
|
||||
|
||||
//go:linkname moduledataverify1 runtime.moduledataverify1
|
||||
func moduledataverify1(_ *_ModuleData)
|
||||
|
||||
// PCDATA and FUNCDATA table indexes.
|
||||
//
|
||||
// See funcdata.h and $GROOT/src/cmd/internal/objabi/funcdata.go.
|
||||
const (
|
||||
_FUNCDATA_ArgsPointerMaps = 0
|
||||
_FUNCDATA_LocalsPointerMaps = 1
|
||||
)
|
||||
|
||||
type funcInfo struct {
|
||||
*_Func
|
||||
datap *_ModuleData
|
||||
}
|
||||
|
||||
//go:linkname findfunc runtime.findfunc
|
||||
func findfunc(pc uintptr) funcInfo
|
||||
|
||||
//go:linkname funcdata runtime.funcdata
|
||||
func funcdata(f funcInfo, i uint8) unsafe.Pointer
|
||||
|
||||
var (
|
||||
modLock sync.Mutex
|
||||
modList []*_ModuleData
|
||||
)
|
||||
|
||||
var emptyByte byte
|
||||
|
||||
func encodeVariant(v int) []byte {
|
||||
var u int
|
||||
var r []byte
|
||||
|
||||
/* split every 7 bits */
|
||||
for v > 127 {
|
||||
u = v & 0x7f
|
||||
v = v >> 7
|
||||
r = append(r, byte(u) | 0x80)
|
||||
}
|
||||
|
||||
/* check for last one */
|
||||
if v == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
/* add the last one */
|
||||
r = append(r, byte(v))
|
||||
return r
|
||||
}
|
||||
|
||||
func registerModule(mod *_ModuleData) {
|
||||
modLock.Lock()
|
||||
modList = append(modList, mod)
|
||||
lastmoduledatap.next = mod
|
||||
lastmoduledatap = mod
|
||||
modLock.Unlock()
|
||||
}
|
||||
|
||||
func stackMap(f interface{}) (args uintptr, locals uintptr) {
|
||||
fv := reflect.ValueOf(f)
|
||||
if fv.Kind() != reflect.Func {
|
||||
panic("f must be reflect.Func kind!")
|
||||
}
|
||||
fi := findfunc(fv.Pointer())
|
||||
return uintptr(funcdata(fi, uint8(_FUNCDATA_ArgsPointerMaps))), uintptr(funcdata(fi, uint8(_FUNCDATA_LocalsPointerMaps)))
|
||||
}
|
||||
|
||||
var moduleCache = struct{
|
||||
m map[*_ModuleData][]byte
|
||||
l sync.Mutex
|
||||
}{
|
||||
m : make(map[*_ModuleData][]byte),
|
||||
}
|
||||
|
||||
func cacheStackmap(argPtrs []bool, localPtrs []bool, mod *_ModuleData) (argptrs uintptr, localptrs uintptr) {
|
||||
as := rt.StackMapBuilder{}
|
||||
for _, b := range argPtrs {
|
||||
as.AddField(b)
|
||||
}
|
||||
ab, _ := as.Build().MarshalBinary()
|
||||
ls := rt.StackMapBuilder{}
|
||||
for _, b := range localPtrs {
|
||||
ls.AddField(b)
|
||||
}
|
||||
lb, _ := ls.Build().MarshalBinary()
|
||||
cache := make([]byte, len(ab) + len(lb))
|
||||
copy(cache, ab)
|
||||
copy(cache[len(ab):], lb)
|
||||
moduleCache.l.Lock()
|
||||
moduleCache.m[mod] = cache
|
||||
moduleCache.l.Unlock()
|
||||
return uintptr(rt.IndexByte(cache, 0)), uintptr(rt.IndexByte(cache, len(ab)))
|
||||
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
// +build go1.15,!go1.16
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
type _Func struct {
|
||||
entry uintptr // start pc
|
||||
nameoff int32 // function name
|
||||
args int32 // in/out args size
|
||||
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||
pcsp int32
|
||||
pcfile int32
|
||||
pcln int32
|
||||
npcdata int32
|
||||
funcID uint8 // set for certain special runtime functions
|
||||
_ [2]int8 // unused
|
||||
nfuncdata uint8 // must be last
|
||||
argptrs uintptr
|
||||
localptrs uintptr
|
||||
}
|
||||
|
||||
type _FuncTab struct {
|
||||
entry uintptr
|
||||
funcoff uintptr
|
||||
}
|
||||
|
||||
type _BitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type _PtabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type _TextSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
length uintptr // section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type _ModuleData struct {
|
||||
pclntable []byte
|
||||
ftab []_FuncTab
|
||||
filetab []uint32
|
||||
findfunctab *_FindFuncBucket
|
||||
minpc, maxpc uintptr
|
||||
text, etext uintptr
|
||||
noptrdata, enoptrdata uintptr
|
||||
data, edata uintptr
|
||||
bss, ebss uintptr
|
||||
noptrbss, enoptrbss uintptr
|
||||
end, gcdata, gcbss uintptr
|
||||
types, etypes uintptr
|
||||
textsectmap []_TextSection
|
||||
typelinks []int32 // offsets from types
|
||||
itablinks []*rt.GoItab
|
||||
ptab []_PtabEntry
|
||||
pluginpath string
|
||||
pkghashes []byte
|
||||
modulename string
|
||||
modulehashes []byte
|
||||
hasmain uint8 // 1 if module contains the main function, 0 otherwise
|
||||
gcdatamask, gcbssmask _BitVector
|
||||
typemap map[int32]*rt.GoType // offset to *_rtype in previous module
|
||||
bad bool // module failed to load and should be ignored
|
||||
next *_ModuleData
|
||||
}
|
||||
|
||||
type _FindFuncBucket struct {
|
||||
idx uint32
|
||||
subbuckets [16]byte
|
||||
}
|
||||
|
||||
var findFuncTab = &_FindFuncBucket {
|
||||
idx: 1,
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||
mod := new(_ModuleData)
|
||||
minpc := pc
|
||||
maxpc := pc + size
|
||||
|
||||
/* build the PC & line table */
|
||||
pclnt := []byte {
|
||||
0xfb, 0xff, 0xff, 0xff, // magic : 0xfffffffb
|
||||
0, // pad1 : 0
|
||||
0, // pad2 : 0
|
||||
1, // minLC : 1
|
||||
4 << (^uintptr(0) >> 63), // ptrSize : 4 << (^uintptr(0) >> 63)
|
||||
}
|
||||
|
||||
// cache arg and local stackmap
|
||||
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||
|
||||
/* add the function name */
|
||||
noff := len(pclnt)
|
||||
pclnt = append(append(pclnt, name...), 0)
|
||||
|
||||
/* add PCDATA */
|
||||
pcsp := len(pclnt)
|
||||
pclnt = append(pclnt, encodeVariant((fp + 1) << 1)...)
|
||||
pclnt = append(pclnt, encodeVariant(int(size))...)
|
||||
|
||||
/* function entry */
|
||||
fnv := _Func {
|
||||
entry : pc,
|
||||
nameoff : int32(noff),
|
||||
args : int32(args),
|
||||
pcsp : int32(pcsp),
|
||||
nfuncdata : 2,
|
||||
argptrs : uintptr(argptrs),
|
||||
localptrs : uintptr(localptrs),
|
||||
}
|
||||
|
||||
/* align the func to 8 bytes */
|
||||
if p := len(pclnt) % 8; p != 0 {
|
||||
pclnt = append(pclnt, make([]byte, 8 - p)...)
|
||||
}
|
||||
|
||||
/* add the function descriptor */
|
||||
foff := len(pclnt)
|
||||
pclnt = append(pclnt, (*(*[unsafe.Sizeof(_Func{})]byte)(unsafe.Pointer(&fnv)))[:]...)
|
||||
|
||||
/* function table */
|
||||
tab := []_FuncTab {
|
||||
{entry: pc, funcoff: uintptr(foff)},
|
||||
{entry: pc, funcoff: uintptr(foff)},
|
||||
{entry: maxpc},
|
||||
}
|
||||
|
||||
/* module data */
|
||||
*mod = _ModuleData {
|
||||
pclntable : pclnt,
|
||||
ftab : tab,
|
||||
findfunctab : findFuncTab,
|
||||
minpc : minpc,
|
||||
maxpc : maxpc,
|
||||
modulename : name,
|
||||
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
}
|
||||
|
||||
/* verify and register the new module */
|
||||
moduledataverify1(mod)
|
||||
registerModule(mod)
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
//go:build go1.16 && !go1.18
|
||||
// +build go1.16,!go1.18
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
type _Func struct {
|
||||
entry uintptr // start pc
|
||||
nameoff int32 // function name
|
||||
args int32 // in/out args size
|
||||
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||
pcsp uint32
|
||||
pcfile uint32
|
||||
pcln uint32
|
||||
npcdata uint32
|
||||
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||
funcID uint8 // set for certain special runtime functions
|
||||
_ [2]byte // pad
|
||||
nfuncdata uint8 // must be last
|
||||
argptrs uintptr
|
||||
localptrs uintptr
|
||||
}
|
||||
|
||||
type _FuncTab struct {
|
||||
entry uintptr
|
||||
funcoff uintptr
|
||||
}
|
||||
|
||||
type _PCHeader struct {
|
||||
magic uint32 // 0xFFFFFFFA
|
||||
pad1, pad2 uint8 // 0,0
|
||||
minLC uint8 // min instruction size
|
||||
ptrSize uint8 // size of a ptr in bytes
|
||||
nfunc int // number of functions in the module
|
||||
nfiles uint // number of entries in the file tab.
|
||||
funcnameOffset uintptr // offset to the funcnametab variable from _PCHeader
|
||||
cuOffset uintptr // offset to the cutab variable from _PCHeader
|
||||
filetabOffset uintptr // offset to the filetab variable from _PCHeader
|
||||
pctabOffset uintptr // offset to the pctab varible from _PCHeader
|
||||
pclnOffset uintptr // offset to the pclntab variable from _PCHeader
|
||||
}
|
||||
|
||||
type _BitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type _PtabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type _TextSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
length uintptr // section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type _ModuleData struct {
|
||||
pcHeader *_PCHeader
|
||||
funcnametab []byte
|
||||
cutab []uint32
|
||||
filetab []byte
|
||||
pctab []byte
|
||||
pclntable []_Func
|
||||
ftab []_FuncTab
|
||||
findfunctab *_FindFuncBucket
|
||||
minpc, maxpc uintptr
|
||||
text, etext uintptr
|
||||
noptrdata, enoptrdata uintptr
|
||||
data, edata uintptr
|
||||
bss, ebss uintptr
|
||||
noptrbss, enoptrbss uintptr
|
||||
end, gcdata, gcbss uintptr
|
||||
types, etypes uintptr
|
||||
textsectmap []_TextSection
|
||||
typelinks []int32
|
||||
itablinks []unsafe.Pointer
|
||||
ptab []_PtabEntry
|
||||
pluginpath string
|
||||
pkghashes []struct{}
|
||||
modulename string
|
||||
modulehashes []struct{}
|
||||
hasmain uint8
|
||||
gcdatamask, gcbssmask _BitVector
|
||||
typemap map[int32]unsafe.Pointer
|
||||
bad bool
|
||||
next *_ModuleData
|
||||
}
|
||||
|
||||
type _FindFuncBucket struct {
|
||||
idx uint32
|
||||
subbuckets [16]byte
|
||||
}
|
||||
|
||||
var modHeader = &_PCHeader {
|
||||
magic : 0xfffffffa,
|
||||
minLC : 1,
|
||||
nfunc : 1,
|
||||
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||
}
|
||||
|
||||
var findFuncTab = &_FindFuncBucket {
|
||||
idx: 1,
|
||||
}
|
||||
|
||||
func makePCtab(fp int) []byte {
|
||||
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||
mod := new(_ModuleData)
|
||||
|
||||
minpc := pc
|
||||
maxpc := pc + size
|
||||
|
||||
// cache arg and local stackmap
|
||||
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||
|
||||
/* function entry */
|
||||
lnt := []_Func {{
|
||||
entry : pc,
|
||||
nameoff : 1,
|
||||
args : int32(args),
|
||||
pcsp : 1,
|
||||
nfuncdata : 2,
|
||||
argptrs : uintptr(argptrs),
|
||||
localptrs : uintptr(localptrs),
|
||||
}}
|
||||
|
||||
/* function table */
|
||||
tab := []_FuncTab {
|
||||
{entry: pc},
|
||||
{entry: pc},
|
||||
{entry: maxpc},
|
||||
}
|
||||
|
||||
/* module data */
|
||||
*mod = _ModuleData {
|
||||
pcHeader : modHeader,
|
||||
funcnametab : append(append([]byte{0}, name...), 0),
|
||||
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||
pclntable : lnt,
|
||||
ftab : tab,
|
||||
findfunctab : findFuncTab,
|
||||
minpc : minpc,
|
||||
maxpc : maxpc,
|
||||
modulename : name,
|
||||
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
}
|
||||
|
||||
/* verify and register the new module */
|
||||
moduledataverify1(mod)
|
||||
registerModule(mod)
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
// +build go1.18,!go1.20
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
// A FuncFlag holds bits about a function.
|
||||
// This list must match the list in cmd/internal/objabi/funcid.go.
|
||||
type funcFlag uint8
|
||||
|
||||
type _Func struct {
|
||||
entryOff uint32 // start pc
|
||||
nameoff int32 // function name
|
||||
args int32 // in/out args size
|
||||
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||
pcsp uint32
|
||||
pcfile uint32
|
||||
pcln uint32
|
||||
npcdata uint32
|
||||
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||
funcID uint8 // set for certain special runtime functions
|
||||
flag funcFlag
|
||||
_ [1]byte // pad
|
||||
nfuncdata uint8 // must be last
|
||||
argptrs uint32
|
||||
localptrs uint32
|
||||
}
|
||||
|
||||
type _FuncTab struct {
|
||||
entry uint32
|
||||
funcoff uint32
|
||||
}
|
||||
|
||||
type _PCHeader struct {
|
||||
magic uint32 // 0xFFFFFFF0
|
||||
pad1, pad2 uint8 // 0,0
|
||||
minLC uint8 // min instruction size
|
||||
ptrSize uint8 // size of a ptr in bytes
|
||||
nfunc int // number of functions in the module
|
||||
nfiles uint // number of entries in the file tab
|
||||
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||
}
|
||||
|
||||
type _BitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type _PtabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type _TextSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
length uintptr // section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type _ModuleData struct {
|
||||
pcHeader *_PCHeader
|
||||
funcnametab []byte
|
||||
cutab []uint32
|
||||
filetab []byte
|
||||
pctab []byte
|
||||
pclntable []byte
|
||||
ftab []_FuncTab
|
||||
findfunctab *_FindFuncBucket
|
||||
minpc, maxpc uintptr
|
||||
text, etext uintptr
|
||||
noptrdata, enoptrdata uintptr
|
||||
data, edata uintptr
|
||||
bss, ebss uintptr
|
||||
noptrbss, enoptrbss uintptr
|
||||
end, gcdata, gcbss uintptr
|
||||
types, etypes uintptr
|
||||
rodata uintptr
|
||||
gofunc uintptr
|
||||
textsectmap []_TextSection
|
||||
typelinks []int32
|
||||
itablinks []unsafe.Pointer
|
||||
ptab []_PtabEntry
|
||||
pluginpath string
|
||||
pkghashes []struct{}
|
||||
modulename string
|
||||
modulehashes []struct{}
|
||||
hasmain uint8
|
||||
gcdatamask, gcbssmask _BitVector
|
||||
typemap map[int32]unsafe.Pointer
|
||||
bad bool
|
||||
next *_ModuleData
|
||||
}
|
||||
|
||||
|
||||
type _FindFuncBucket struct {
|
||||
idx uint32
|
||||
subbuckets [16]byte
|
||||
}
|
||||
|
||||
|
||||
|
||||
func makePCtab(fp int) []byte {
|
||||
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||
mod := new(_ModuleData)
|
||||
|
||||
minpc := pc
|
||||
maxpc := pc + size
|
||||
|
||||
findFuncTab := make([]_FindFuncBucket, textSize/4096 + 1)
|
||||
|
||||
modHeader := &_PCHeader {
|
||||
magic : 0xfffffff0,
|
||||
minLC : 1,
|
||||
nfunc : 1,
|
||||
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||
textStart: minpc,
|
||||
}
|
||||
|
||||
// cache arg and local stackmap
|
||||
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||
|
||||
base := argptrs
|
||||
if argptrs > localptrs {
|
||||
base = localptrs
|
||||
}
|
||||
|
||||
/* function entry */
|
||||
lnt := []_Func {{
|
||||
entryOff : 0,
|
||||
nameoff : 1,
|
||||
args : int32(args),
|
||||
pcsp : 1,
|
||||
nfuncdata : 2,
|
||||
argptrs: uint32(argptrs - base),
|
||||
localptrs: uint32(localptrs - base),
|
||||
}}
|
||||
nlnt := len(lnt)*int(unsafe.Sizeof(_Func{}))
|
||||
plnt := unsafe.Pointer(&lnt[0])
|
||||
|
||||
/* function table */
|
||||
ftab := []_FuncTab {
|
||||
{entry : 0, funcoff : 16},
|
||||
{entry : uint32(size)},
|
||||
}
|
||||
nftab := len(ftab)*int(unsafe.Sizeof(_FuncTab{}))
|
||||
pftab := unsafe.Pointer(&ftab[0])
|
||||
|
||||
pclntab := make([]byte, 0, nftab + nlnt)
|
||||
pclntab = append(pclntab, rt.BytesFrom(pftab, nftab, nftab)...)
|
||||
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||
|
||||
/* module data */
|
||||
*mod = _ModuleData {
|
||||
pcHeader : modHeader,
|
||||
funcnametab : append(append([]byte{0}, name...), 0),
|
||||
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||
pclntable : pclntab,
|
||||
ftab : ftab,
|
||||
text : minpc,
|
||||
etext : pc + textSize,
|
||||
findfunctab : &findFuncTab[0],
|
||||
minpc : minpc,
|
||||
maxpc : maxpc,
|
||||
modulename : name,
|
||||
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gofunc: base,
|
||||
}
|
||||
|
||||
/* verify and register the new module */
|
||||
moduledataverify1(mod)
|
||||
registerModule(mod)
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
// +build go1.20
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
// A FuncFlag holds bits about a function.
|
||||
// This list must match the list in cmd/internal/objabi/funcid.go.
|
||||
type funcFlag uint8
|
||||
|
||||
type _Func struct {
|
||||
entryOff uint32 // start pc
|
||||
nameoff int32 // function name
|
||||
args int32 // in/out args size
|
||||
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||
pcsp uint32
|
||||
pcfile uint32
|
||||
pcln uint32
|
||||
npcdata uint32
|
||||
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||
funcID uint8 // set for certain special runtime functions
|
||||
flag funcFlag
|
||||
_ [1]byte // pad
|
||||
nfuncdata uint8 // must be last
|
||||
argptrs uint32
|
||||
localptrs uint32
|
||||
}
|
||||
|
||||
type _FuncTab struct {
|
||||
entry uint32
|
||||
funcoff uint32
|
||||
}
|
||||
|
||||
type _PCHeader struct {
|
||||
magic uint32 // 0xFFFFFFF0
|
||||
pad1, pad2 uint8 // 0,0
|
||||
minLC uint8 // min instruction size
|
||||
ptrSize uint8 // size of a ptr in bytes
|
||||
nfunc int // number of functions in the module
|
||||
nfiles uint // number of entries in the file tab
|
||||
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||
}
|
||||
|
||||
type _BitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type _PtabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type _TextSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
length uintptr // section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type _ModuleData struct {
|
||||
pcHeader *_PCHeader
|
||||
funcnametab []byte
|
||||
cutab []uint32
|
||||
filetab []byte
|
||||
pctab []byte
|
||||
pclntable []byte
|
||||
ftab []_FuncTab
|
||||
findfunctab *_FindFuncBucket
|
||||
minpc, maxpc uintptr
|
||||
text, etext uintptr
|
||||
noptrdata, enoptrdata uintptr
|
||||
data, edata uintptr
|
||||
bss, ebss uintptr
|
||||
noptrbss, enoptrbss uintptr
|
||||
end, gcdata, gcbss uintptr
|
||||
types, etypes uintptr
|
||||
rodata uintptr
|
||||
gofunc uintptr
|
||||
textsectmap []_TextSection
|
||||
typelinks []int32
|
||||
itablinks []unsafe.Pointer
|
||||
ptab []_PtabEntry
|
||||
pluginpath string
|
||||
pkghashes []struct{}
|
||||
modulename string
|
||||
modulehashes []struct{}
|
||||
hasmain uint8
|
||||
gcdatamask, gcbssmask _BitVector
|
||||
typemap map[int32]unsafe.Pointer
|
||||
bad bool
|
||||
next *_ModuleData
|
||||
}
|
||||
|
||||
|
||||
type _FindFuncBucket struct {
|
||||
idx uint32
|
||||
subbuckets [16]byte
|
||||
}
|
||||
|
||||
|
||||
|
||||
func makePCtab(fp int) []byte {
|
||||
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||
mod := new(_ModuleData)
|
||||
|
||||
minpc := pc
|
||||
maxpc := pc + size
|
||||
|
||||
findFuncTab := make([]_FindFuncBucket, textSize/4096 + 1)
|
||||
|
||||
modHeader := &_PCHeader {
|
||||
magic : 0xfffffff0,
|
||||
minLC : 1,
|
||||
nfunc : 1,
|
||||
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||
textStart: minpc,
|
||||
}
|
||||
|
||||
// cache arg and local stackmap
|
||||
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||
|
||||
base := argptrs
|
||||
if argptrs > localptrs {
|
||||
base = localptrs
|
||||
}
|
||||
|
||||
/* function entry */
|
||||
lnt := []_Func {{
|
||||
entryOff : 0,
|
||||
nameoff : 1,
|
||||
args : int32(args),
|
||||
pcsp : 1,
|
||||
nfuncdata : 2,
|
||||
argptrs: uint32(argptrs - base),
|
||||
localptrs: uint32(localptrs - base),
|
||||
}}
|
||||
nlnt := len(lnt)*int(unsafe.Sizeof(_Func{}))
|
||||
plnt := unsafe.Pointer(&lnt[0])
|
||||
|
||||
/* function table */
|
||||
ftab := []_FuncTab {
|
||||
{entry : 0, funcoff : 16},
|
||||
{entry : uint32(size)},
|
||||
}
|
||||
nftab := len(ftab)*int(unsafe.Sizeof(_FuncTab{}))
|
||||
pftab := unsafe.Pointer(&ftab[0])
|
||||
|
||||
pclntab := make([]byte, 0, nftab + nlnt)
|
||||
pclntab = append(pclntab, rt.BytesFrom(pftab, nftab, nftab)...)
|
||||
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||
|
||||
/* module data */
|
||||
*mod = _ModuleData {
|
||||
pcHeader : modHeader,
|
||||
funcnametab : append(append([]byte{0}, name...), 0),
|
||||
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||
pclntable : pclntab,
|
||||
ftab : ftab,
|
||||
text : minpc,
|
||||
etext : pc + textSize,
|
||||
findfunctab : &findFuncTab[0],
|
||||
minpc : minpc,
|
||||
maxpc : maxpc,
|
||||
modulename : name,
|
||||
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||
gofunc: base,
|
||||
}
|
||||
|
||||
/* verify and register the new module */
|
||||
moduledataverify1(mod)
|
||||
registerModule(mod)
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
//go:build darwin || linux
|
||||
// +build darwin linux
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`os`
|
||||
`reflect`
|
||||
`syscall`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
const (
|
||||
_AP = syscall.MAP_ANON | syscall.MAP_PRIVATE
|
||||
_RX = syscall.PROT_READ | syscall.PROT_EXEC
|
||||
_RW = syscall.PROT_READ | syscall.PROT_WRITE
|
||||
)
|
||||
|
||||
type Loader []byte
|
||||
type Function unsafe.Pointer
|
||||
|
||||
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||
p := os.Getpagesize()
|
||||
n := (((len(self) - 1) / p) + 1) * p
|
||||
|
||||
/* register the function */
|
||||
m := mmap(n)
|
||||
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||
|
||||
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||
|
||||
/* reference as a slice */
|
||||
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||
Data : m,
|
||||
Cap : n,
|
||||
Len : len(self),
|
||||
}))
|
||||
|
||||
/* copy the machine code, and make it executable */
|
||||
copy(s, self)
|
||||
mprotect(m, n)
|
||||
return Function(&m)
|
||||
}
|
||||
|
||||
func mmap(nb int) uintptr {
|
||||
if m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(nb), _RW, _AP, 0, 0); e != 0 {
|
||||
panic(e)
|
||||
} else {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func mprotect(p uintptr, nb int) {
|
||||
if _, _, err := syscall.RawSyscall(syscall.SYS_MPROTECT, p, uintptr(nb), _RX); err != 0 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`os`
|
||||
`reflect`
|
||||
`syscall`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
const (
|
||||
MEM_COMMIT = 0x00001000
|
||||
MEM_RESERVE = 0x00002000
|
||||
)
|
||||
|
||||
var (
|
||||
libKernel32 = syscall.NewLazyDLL("KERNEL32.DLL")
|
||||
libKernel32_VirtualAlloc = libKernel32.NewProc("VirtualAlloc")
|
||||
libKernel32_VirtualProtect = libKernel32.NewProc("VirtualProtect")
|
||||
)
|
||||
|
||||
type Loader []byte
|
||||
type Function unsafe.Pointer
|
||||
|
||||
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||
p := os.Getpagesize()
|
||||
n := (((len(self) - 1) / p) + 1) * p
|
||||
|
||||
/* register the function */
|
||||
m := mmap(n)
|
||||
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||
|
||||
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||
|
||||
/* reference as a slice */
|
||||
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||
Data : m,
|
||||
Cap : n,
|
||||
Len : len(self),
|
||||
}))
|
||||
|
||||
/* copy the machine code, and make it executable */
|
||||
copy(s, self)
|
||||
mprotect(m, n)
|
||||
return Function(&m)
|
||||
}
|
||||
|
||||
func mmap(nb int) uintptr {
|
||||
addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func mprotect(p uintptr, nb int) (oldProtect int) {
|
||||
err := winapi_VirtualProtect(p, nb, syscall.PAGE_EXECUTE_READ, &oldProtect)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// winapi_VirtualAlloc allocate memory
|
||||
// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
|
||||
func winapi_VirtualAlloc(lpAddr uintptr, dwSize int, flAllocationType int, flProtect int) (uintptr, error) {
|
||||
r1, _, err := libKernel32_VirtualAlloc.Call(
|
||||
lpAddr,
|
||||
uintptr(dwSize),
|
||||
uintptr(flAllocationType),
|
||||
uintptr(flProtect),
|
||||
)
|
||||
if r1 == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return r1, nil
|
||||
}
|
||||
|
||||
// winapi_VirtualProtect change memory protection
|
||||
// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect
|
||||
func winapi_VirtualProtect(lpAddr uintptr, dwSize int, flNewProtect int, lpflOldProtect *int) error {
|
||||
r1, _, err := libKernel32_VirtualProtect.Call(
|
||||
lpAddr,
|
||||
uintptr(dwSize),
|
||||
uintptr(flNewProtect),
|
||||
uintptr(unsafe.Pointer(lpflOldProtect)),
|
||||
)
|
||||
if r1 == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
||||
// Code generated by Makefile, DO NOT EDIT.
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package avx
|
||||
|
||||
var (
|
||||
S_f64toa = _subr__f64toa
|
||||
S_f32toa = _subr__f32toa
|
||||
S_i64toa = _subr__i64toa
|
||||
S_u64toa = _subr__u64toa
|
||||
S_lspace = _subr__lspace
|
||||
)
|
||||
|
||||
var (
|
||||
S_quote = _subr__quote
|
||||
S_unquote = _subr__unquote
|
||||
)
|
||||
|
||||
var (
|
||||
S_value = _subr__value
|
||||
S_vstring = _subr__vstring
|
||||
S_vnumber = _subr__vnumber
|
||||
S_vsigned = _subr__vsigned
|
||||
S_vunsigned = _subr__vunsigned
|
||||
)
|
||||
|
||||
var (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
||||
// Code generated by Makefile, DO NOT EDIT.
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package avx2
|
||||
|
||||
var (
|
||||
S_f64toa = _subr__f64toa
|
||||
S_f32toa = _subr__f32toa
|
||||
S_i64toa = _subr__i64toa
|
||||
S_u64toa = _subr__u64toa
|
||||
S_lspace = _subr__lspace
|
||||
)
|
||||
|
||||
var (
|
||||
S_quote = _subr__quote
|
||||
S_unquote = _subr__unquote
|
||||
)
|
||||
|
||||
var (
|
||||
S_value = _subr__value
|
||||
S_vstring = _subr__vstring
|
||||
S_vnumber = _subr__vnumber
|
||||
S_vsigned = _subr__vsigned
|
||||
S_vunsigned = _subr__vunsigned
|
||||
)
|
||||
|
||||
var (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
14800
vendor/github.com/bytedance/sonic/internal/native/avx2/native_text_amd64.go
generated
vendored
14800
vendor/github.com/bytedance/sonic/internal/native/avx2/native_text_amd64.go
generated
vendored
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package {{PACKAGE}}
|
||||
|
||||
var (
|
||||
S_f64toa = _subr__f64toa
|
||||
S_f32toa = _subr__f32toa
|
||||
S_i64toa = _subr__i64toa
|
||||
S_u64toa = _subr__u64toa
|
||||
S_lspace = _subr__lspace
|
||||
)
|
||||
|
||||
var (
|
||||
S_quote = _subr__quote
|
||||
S_unquote = _subr__unquote
|
||||
)
|
||||
|
||||
var (
|
||||
S_value = _subr__value
|
||||
S_vstring = _subr__vstring
|
||||
S_vnumber = _subr__vnumber
|
||||
S_vsigned = _subr__vsigned
|
||||
S_vunsigned = _subr__vunsigned
|
||||
)
|
||||
|
||||
var (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
@ -0,0 +1,697 @@
|
||||
/**
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package {{PACKAGE}}
|
||||
|
||||
import (
|
||||
`os`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`testing`
|
||||
`time`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/loader`
|
||||
)
|
||||
|
||||
var (
|
||||
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||
)
|
||||
|
||||
var stubs = []loader.GoC{
|
||||
{"_f32toa", nil, &__f32toa},
|
||||
{"_f64toa", nil, &__f64toa},
|
||||
{"_fsm_exec", nil, &__fsm_exec},
|
||||
{"_get_by_path", nil, &__get_by_path},
|
||||
{"_html_escape", nil, &__html_escape},
|
||||
{"_i64toa", nil, &__i64toa},
|
||||
{"_lspace", nil, &__lspace},
|
||||
{"_quote", nil, &__quote},
|
||||
{"_skip_array", nil, &__skip_array},
|
||||
{"_skip_number", nil, &__skip_number},
|
||||
{"_skip_object", nil, &__skip_object},
|
||||
{"_skip_one", nil, &__skip_one},
|
||||
{"_skip_one_fast", nil, &__skip_one_fast},
|
||||
{"_u64toa", nil, &__u64toa},
|
||||
{"_unquote", nil, &__unquote},
|
||||
{"_validate_one", nil, &__validate_one},
|
||||
{"_validate_utf8", nil, &__validate_utf8},
|
||||
{"_validate_utf8_fast", nil, &__validate_utf8_fast},
|
||||
{"_value", nil, &__value},
|
||||
{"_vnumber", nil, &__vnumber},
|
||||
{"_vsigned", nil, &__vsigned},
|
||||
{"_vstring", nil, &__vstring},
|
||||
{"_vunsigned", nil, &__vunsigned},
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
loader.WrapGoC(Text__native_entry__, Funcs, stubs, "{{PACKAGE}}", "{{PACKAGE}}/native.c")
|
||||
|
||||
go func () {
|
||||
if !debugAsyncGC {
|
||||
return
|
||||
}
|
||||
println("Begin GC looping...")
|
||||
for {
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
println("stop GC looping!")
|
||||
}()
|
||||
time.Sleep(time.Millisecond*100)
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestRecover_f64toa(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = f64toa(nil, 123)
|
||||
}
|
||||
|
||||
func TestRecover_f32toa(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = f32toa(nil, 123)
|
||||
}
|
||||
|
||||
func TestRecover_i64toa(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = i64toa(nil, 123)
|
||||
}
|
||||
|
||||
func TestRecover_u64toa(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = u64toa(nil, 123)
|
||||
}
|
||||
|
||||
func TestRecover_lspace(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = lspace(nil, 2, 0)
|
||||
}
|
||||
|
||||
func TestRecover_quote(t *testing.T) {
|
||||
var dn = 10
|
||||
var dp = make([]byte, dn)
|
||||
var sp = []byte("123")
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = quote(nil, 3, unsafe.Pointer(&dp[0]), &dn, 0)
|
||||
})
|
||||
t.Run("dp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = quote(unsafe.Pointer(&sp[0]), 3, nil, &dn, 0)
|
||||
})
|
||||
t.Run("dn", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = quote(unsafe.Pointer(&sp[0]), 3, unsafe.Pointer(&dp[0]), nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_html_escape(t *testing.T) {
|
||||
var dn = 10
|
||||
var dp = make([]byte, dn)
|
||||
var sp = []byte("123")
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = html_escape(nil, 3, unsafe.Pointer(&dp[0]), &dn)
|
||||
})
|
||||
t.Run("dp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = html_escape(unsafe.Pointer(&sp[0]), 3, nil, &dn)
|
||||
})
|
||||
t.Run("dn", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = html_escape(unsafe.Pointer(&sp[0]), 3, unsafe.Pointer(&dp[0]), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_unquote(t *testing.T) {
|
||||
var ep = 0
|
||||
var dp = make([]byte, 10)
|
||||
var sp = []byte("12\\x\"3\"4")
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = unquote(nil, len(sp), unsafe.Pointer(&dp[0]), &ep, 0)
|
||||
})
|
||||
t.Run("dp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = unquote(unsafe.Pointer(&sp[0]), len(sp), nil, &ep, 0)
|
||||
})
|
||||
t.Run("ep", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = unquote(unsafe.Pointer(&sp[0]), len(sp), unsafe.Pointer(&dp[0]), nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_value(t *testing.T) {
|
||||
var v = new(types.JsonState)
|
||||
var sp = []byte("123")
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = value(nil, 3, 0, v, 0)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = value(unsafe.Pointer(&sp[0]), 3, 0, nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_vstring(t *testing.T) {
|
||||
var v = new(types.JsonState)
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vstring(nil, &p, v, 0)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vstring(&sp, nil, v, 0)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vstring(&sp, &p, nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_vnumber(t *testing.T) {
|
||||
var v = new(types.JsonState)
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vnumber(nil, &p, v)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vnumber(&sp, nil, v)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vnumber(&sp, &p, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_vsigned(t *testing.T) {
|
||||
var v = new(types.JsonState)
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vsigned(nil, &p, v)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vsigned(&sp, nil, v)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vsigned(&sp, &p, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_vunsigned(t *testing.T) {
|
||||
var v = new(types.JsonState)
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vunsigned(nil, &p, v)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vunsigned(&sp, nil, v)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
vunsigned(&sp, &p, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_skip_one(t *testing.T) {
|
||||
var v = types.NewStateMachine()
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_one(nil, &p, v, 0)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_one(&sp, nil, v, 0)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_one(&sp, &p, nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_skip_one_fast(t *testing.T) {
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_one_fast(nil, &p)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_one_fast(&sp, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_skip_array(t *testing.T) {
|
||||
var v = types.NewStateMachine()
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_array(nil, &p, v, 0)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_array(&sp, nil, v, 0)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_array(&sp, &p, nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_skip_object(t *testing.T) {
|
||||
var v = types.NewStateMachine()
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_object(nil, &p, v, 0)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_object(&sp, nil, v, 0)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_object(&sp, &p, nil, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_skip_number(t *testing.T) {
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_number(nil, &p)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = skip_number(&sp, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_get_by_path(t *testing.T) {
|
||||
var v = []interface{}{}
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
var m = types.NewStateMachine()
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = get_by_path(nil, &p, &v, m)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = get_by_path(&sp, nil, &v, m)
|
||||
})
|
||||
t.Run("path", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = get_by_path(&sp, &p, nil, m)
|
||||
})
|
||||
t.Run("m", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = get_by_path(&sp, &p, &v, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_validate_one(t *testing.T) {
|
||||
var v = types.NewStateMachine()
|
||||
var sp = "123"
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_one(nil, &p, v)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_one(&sp, nil, v)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_one(&sp, &p, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_validate_utf8(t *testing.T) {
|
||||
var v = types.NewStateMachine()
|
||||
var sp = string([]byte{0xff, 0xff, 0xff})
|
||||
var p = 0
|
||||
t.Run("sp", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_utf8(nil, &p, v)
|
||||
})
|
||||
t.Run("p", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_utf8(&sp, nil, v)
|
||||
})
|
||||
t.Run("v", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_utf8(&sp, &p, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecover_validate_utf8_fast(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r!= nil {
|
||||
t.Log("recover: ", r)
|
||||
} else {
|
||||
t.Fatal("no panic")
|
||||
}
|
||||
}()
|
||||
_ = validate_utf8_fast(nil)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
||||
// Code generated by Makefile, DO NOT EDIT.
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package sse
|
||||
|
||||
var (
|
||||
S_f64toa = _subr__f64toa
|
||||
S_f32toa = _subr__f32toa
|
||||
S_i64toa = _subr__i64toa
|
||||
S_u64toa = _subr__u64toa
|
||||
S_lspace = _subr__lspace
|
||||
)
|
||||
|
||||
var (
|
||||
S_quote = _subr__quote
|
||||
S_unquote = _subr__unquote
|
||||
)
|
||||
|
||||
var (
|
||||
S_value = _subr__value
|
||||
S_vstring = _subr__vstring
|
||||
S_vnumber = _subr__vnumber
|
||||
S_vsigned = _subr__vsigned
|
||||
S_vunsigned = _subr__vunsigned
|
||||
)
|
||||
|
||||
var (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
||||
//go:build !go1.16
|
||||
// +build !go1.16
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/loader`
|
||||
)
|
||||
|
||||
// LoadFuncs loads only one function as module, and returns the function pointer
|
||||
// - text: machine code
|
||||
// - funcName: function name
|
||||
// - frameSize: stack frame size.
|
||||
// - argSize: argument total size (in bytes)
|
||||
// - argPtrs: indicates if a slot (8 Bytes) of arguments memory stores pointer, from low to high
|
||||
// - localPtrs: indicates if a slot (8 Bytes) of local variants memory stores pointer, from low to high
|
||||
//
|
||||
// WARN:
|
||||
// - the function MUST has fixed SP offset equaling to this, otherwise it go.gentraceback will fail
|
||||
// - the function MUST has only one stack map for all arguments and local variants
|
||||
func (self Loader) LoadOne(text []byte, funcName string, frameSize int, argSize int, argPtrs []bool, localPtrs []bool) Function {
|
||||
return Function(loader.Loader(text).Load(funcName, frameSize, argSize, argPtrs, localPtrs))
|
||||
}
|
||||
|
||||
// Load loads given machine codes and corresponding function information into go moduledata
|
||||
// and returns runnable function pointer
|
||||
// WARN: this API is experimental, use it carefully
|
||||
func Load(text []byte, funcs []Func, modulename string, filenames []string) (out []Function) {
|
||||
panic("not implemented")
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/abi`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
var _C_Redzone = []bool{false, false, false, false}
|
||||
|
||||
// CFunc is a function information for C func
|
||||
type CFunc struct {
|
||||
// C function name
|
||||
Name string
|
||||
|
||||
// entry pc relative to entire text segment
|
||||
EntryOff uint32
|
||||
|
||||
// function text size in bytes
|
||||
TextSize uint32
|
||||
|
||||
// maximum stack depth of the function
|
||||
MaxStack uintptr
|
||||
|
||||
// PC->SP delta lists of the function
|
||||
Pcsp [][2]uint32
|
||||
}
|
||||
|
||||
// GoC is the wrapper for Go calls to C
|
||||
type GoC struct {
|
||||
// CName is the name of corresponding C function
|
||||
CName string
|
||||
|
||||
// CEntry points out where to store the entry address of corresponding C function.
|
||||
// It won't be set if nil
|
||||
CEntry *uintptr
|
||||
|
||||
// GoFunc is the POINTER of corresponding go stub function.
|
||||
// It is used to generate Go-C ABI conversion wrapper and receive the wrapper's address
|
||||
// eg. &func(a int, b int) int
|
||||
// FOR
|
||||
// int add(int a, int b)
|
||||
// It won't be set if nil
|
||||
GoFunc interface{}
|
||||
}
|
||||
|
||||
// WrapGoC wraps C functions and loader it into Go stubs
|
||||
func WrapGoC(text []byte, natives []CFunc, stubs []GoC, modulename string, filename string) {
|
||||
funcs := make([]Func, len(natives))
|
||||
|
||||
// register C funcs
|
||||
for i, f := range natives {
|
||||
fn := Func{
|
||||
Flag: FuncFlag_ASM,
|
||||
EntryOff: f.EntryOff,
|
||||
TextSize: f.TextSize,
|
||||
Name: f.Name,
|
||||
}
|
||||
if len(f.Pcsp) != 0 {
|
||||
fn.Pcsp = (*Pcdata)(unsafe.Pointer(&natives[i].Pcsp))
|
||||
}
|
||||
// NOTICE: always forbid async preempt
|
||||
fn.PcUnsafePoint = &Pcdata{
|
||||
{PC: f.TextSize, Val: PCDATA_UnsafePointUnsafe},
|
||||
}
|
||||
// NOTICE: always refer to first file
|
||||
fn.Pcfile = &Pcdata{
|
||||
{PC: f.TextSize, Val: 0},
|
||||
}
|
||||
// NOTICE: always refer to first line
|
||||
fn.Pcline = &Pcdata{
|
||||
{PC: f.TextSize, Val: 1},
|
||||
}
|
||||
// NOTICE: copystack need locals stackmap
|
||||
fn.PcStackMapIndex = &Pcdata{
|
||||
{PC: f.TextSize, Val: 0},
|
||||
}
|
||||
sm := rt.StackMapBuilder{}
|
||||
sm.AddField(false)
|
||||
fn.ArgsPointerMaps = sm.Build()
|
||||
fn.LocalsPointerMaps = sm.Build()
|
||||
funcs[i] = fn
|
||||
}
|
||||
rets := Load(text, funcs, modulename, []string{filename})
|
||||
|
||||
// got absolute entry address
|
||||
native_entry := **(**uintptr)(unsafe.Pointer(&rets[0]))
|
||||
// println("native_entry: ", native_entry)
|
||||
|
||||
wraps := make([]Func, 0, len(stubs))
|
||||
wrapIds := make([]int, 0, len(stubs))
|
||||
code := make([]byte, 0, len(wraps))
|
||||
entryOff := uint32(0)
|
||||
|
||||
// register go wrappers
|
||||
for i := range stubs {
|
||||
for j := range natives {
|
||||
if stubs[i].CName != natives[j].Name {
|
||||
continue
|
||||
}
|
||||
|
||||
// calculate corresponding C entry
|
||||
pc := uintptr(native_entry + uintptr(natives[j].EntryOff))
|
||||
if stubs[i].CEntry != nil {
|
||||
*stubs[i].CEntry = pc
|
||||
}
|
||||
|
||||
// no need to generate wrapper, continue next
|
||||
if stubs[i].GoFunc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// assemble wrapper codes
|
||||
layout := abi.NewFunctionLayout(reflect.TypeOf(stubs[i].GoFunc).Elem())
|
||||
frame := abi.NewFrame(&layout, _C_Redzone, true)
|
||||
tcode := abi.CallC(pc, frame, natives[j].MaxStack)
|
||||
code = append(code, tcode...)
|
||||
size := uint32(len(tcode))
|
||||
|
||||
fn := Func{
|
||||
Flag: FuncFlag_ASM,
|
||||
ArgsSize: int32(layout.ArgSize()),
|
||||
EntryOff: entryOff,
|
||||
TextSize: size,
|
||||
Name: stubs[i].CName + "_go",
|
||||
}
|
||||
|
||||
// add check-stack and grow-stack texts' pcsp
|
||||
fn.Pcsp = &Pcdata{
|
||||
{PC: uint32(frame.StackCheckTextSize()), Val: 0},
|
||||
{PC: size - uint32(frame.GrowStackTextSize()), Val: int32(frame.Size())},
|
||||
{PC: size, Val: 0},
|
||||
}
|
||||
// NOTICE: always refer to first file
|
||||
fn.Pcfile = &Pcdata{
|
||||
{PC: size, Val: 0},
|
||||
}
|
||||
// NOTICE: always refer to first line
|
||||
fn.Pcline = &Pcdata{
|
||||
{PC: size, Val: 1},
|
||||
}
|
||||
// NOTICE: always forbid async preempt
|
||||
fn.PcUnsafePoint = &Pcdata{
|
||||
{PC: size, Val: PCDATA_UnsafePointUnsafe},
|
||||
}
|
||||
|
||||
// register pointer stackmaps
|
||||
fn.PcStackMapIndex = &Pcdata{
|
||||
{PC: size, Val: 0},
|
||||
}
|
||||
fn.ArgsPointerMaps = frame.ArgPtrs()
|
||||
fn.LocalsPointerMaps = frame.LocalPtrs()
|
||||
|
||||
entryOff += size
|
||||
wraps = append(wraps, fn)
|
||||
wrapIds = append(wrapIds, i)
|
||||
}
|
||||
}
|
||||
gofuncs := Load(code, wraps, modulename+"/go", []string{filename+".go"})
|
||||
|
||||
// set go func value
|
||||
for i := range gofuncs {
|
||||
idx := wrapIds[i]
|
||||
w := rt.UnpackEface(stubs[idx].GoFunc)
|
||||
*(*Function)(w.Value) = gofuncs[i]
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
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.
|
@ -0,0 +1,245 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
// Type is tyep expression type.
|
||||
type Type int
|
||||
|
||||
const (
|
||||
// CONST indicates that the expression is a constant.
|
||||
CONST Type = iota
|
||||
|
||||
// TERM indicates that the expression is a Term reference.
|
||||
TERM
|
||||
|
||||
// EXPR indicates that the expression is a unary or binary expression.
|
||||
EXPR
|
||||
)
|
||||
|
||||
var typeNames = map[Type]string {
|
||||
EXPR : "Expr",
|
||||
TERM : "Term",
|
||||
CONST : "Const",
|
||||
}
|
||||
|
||||
// String returns the string representation of a Type.
|
||||
func (self Type) String() string {
|
||||
if v, ok := typeNames[self]; ok {
|
||||
return v
|
||||
} else {
|
||||
return fmt.Sprintf("expr.Type(%d)", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Operator represents an operation to perform when Type is EXPR.
|
||||
type Operator uint8
|
||||
|
||||
const (
|
||||
// ADD performs "Add Expr.Left and Expr.Right".
|
||||
ADD Operator = iota
|
||||
|
||||
// SUB performs "Subtract Expr.Left by Expr.Right".
|
||||
SUB
|
||||
|
||||
// MUL performs "Multiply Expr.Left by Expr.Right".
|
||||
MUL
|
||||
|
||||
// DIV performs "Divide Expr.Left by Expr.Right".
|
||||
DIV
|
||||
|
||||
// MOD performs "Modulo Expr.Left by Expr.Right".
|
||||
MOD
|
||||
|
||||
// AND performs "Bitwise AND Expr.Left and Expr.Right".
|
||||
AND
|
||||
|
||||
// OR performs "Bitwise OR Expr.Left and Expr.Right".
|
||||
OR
|
||||
|
||||
// XOR performs "Bitwise XOR Expr.Left and Expr.Right".
|
||||
XOR
|
||||
|
||||
// SHL performs "Bitwise Shift Expr.Left to the Left by Expr.Right Bits".
|
||||
SHL
|
||||
|
||||
// SHR performs "Bitwise Shift Expr.Left to the Right by Expr.Right Bits".
|
||||
SHR
|
||||
|
||||
// POW performs "Raise Expr.Left to the power of Expr.Right"
|
||||
POW
|
||||
|
||||
// NOT performs "Bitwise Invert Expr.Left".
|
||||
NOT
|
||||
|
||||
// NEG performs "Negate Expr.Left".
|
||||
NEG
|
||||
)
|
||||
|
||||
var operatorNames = map[Operator]string {
|
||||
ADD : "Add",
|
||||
SUB : "Subtract",
|
||||
MUL : "Multiply",
|
||||
DIV : "Divide",
|
||||
MOD : "Modulo",
|
||||
AND : "And",
|
||||
OR : "Or",
|
||||
XOR : "ExclusiveOr",
|
||||
SHL : "ShiftLeft",
|
||||
SHR : "ShiftRight",
|
||||
POW : "Power",
|
||||
NOT : "Invert",
|
||||
NEG : "Negate",
|
||||
}
|
||||
|
||||
// String returns the string representation of a Type.
|
||||
func (self Operator) String() string {
|
||||
if v, ok := operatorNames[self]; ok {
|
||||
return v
|
||||
} else {
|
||||
return fmt.Sprintf("expr.Operator(%d)", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Expr represents an expression node.
|
||||
type Expr struct {
|
||||
Type Type
|
||||
Term Term
|
||||
Op Operator
|
||||
Left *Expr
|
||||
Right *Expr
|
||||
Const int64
|
||||
}
|
||||
|
||||
// Ref creates an expression from a Term.
|
||||
func Ref(t Term) (p *Expr) {
|
||||
p = newExpression()
|
||||
p.Term = t
|
||||
p.Type = TERM
|
||||
return
|
||||
}
|
||||
|
||||
// Int creates an expression from an integer.
|
||||
func Int(v int64) (p *Expr) {
|
||||
p = newExpression()
|
||||
p.Type = CONST
|
||||
p.Const = v
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Expr) clear() {
|
||||
if self.Term != nil { self.Term.Free() }
|
||||
if self.Left != nil { self.Left.Free() }
|
||||
if self.Right != nil { self.Right.Free() }
|
||||
}
|
||||
|
||||
// Free returns the Expr into pool.
|
||||
// Any operation performed after Free is undefined behavior.
|
||||
func (self *Expr) Free() {
|
||||
self.clear()
|
||||
freeExpression(self)
|
||||
}
|
||||
|
||||
// Evaluate evaluates the expression into an integer.
|
||||
// It also implements the Term interface.
|
||||
func (self *Expr) Evaluate() (int64, error) {
|
||||
switch self.Type {
|
||||
case EXPR : return self.eval()
|
||||
case TERM : return self.Term.Evaluate()
|
||||
case CONST : return self.Const, nil
|
||||
default : panic("invalid expression type: " + self.Type.String())
|
||||
}
|
||||
}
|
||||
|
||||
/** Expression Combinator **/
|
||||
|
||||
func combine(a *Expr, op Operator, b *Expr) (r *Expr) {
|
||||
r = newExpression()
|
||||
r.Op = op
|
||||
r.Type = EXPR
|
||||
r.Left = a
|
||||
r.Right = b
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Expr) Add(v *Expr) *Expr { return combine(self, ADD, v) }
|
||||
func (self *Expr) Sub(v *Expr) *Expr { return combine(self, SUB, v) }
|
||||
func (self *Expr) Mul(v *Expr) *Expr { return combine(self, MUL, v) }
|
||||
func (self *Expr) Div(v *Expr) *Expr { return combine(self, DIV, v) }
|
||||
func (self *Expr) Mod(v *Expr) *Expr { return combine(self, MOD, v) }
|
||||
func (self *Expr) And(v *Expr) *Expr { return combine(self, AND, v) }
|
||||
func (self *Expr) Or (v *Expr) *Expr { return combine(self, OR , v) }
|
||||
func (self *Expr) Xor(v *Expr) *Expr { return combine(self, XOR, v) }
|
||||
func (self *Expr) Shl(v *Expr) *Expr { return combine(self, SHL, v) }
|
||||
func (self *Expr) Shr(v *Expr) *Expr { return combine(self, SHR, v) }
|
||||
func (self *Expr) Pow(v *Expr) *Expr { return combine(self, POW, v) }
|
||||
func (self *Expr) Not() *Expr { return combine(self, NOT, nil) }
|
||||
func (self *Expr) Neg() *Expr { return combine(self, NEG, nil) }
|
||||
|
||||
/** Expression Evaluator **/
|
||||
|
||||
var binaryEvaluators = [256]func(int64, int64) (int64, error) {
|
||||
ADD: func(a, b int64) (int64, error) { return a + b, nil },
|
||||
SUB: func(a, b int64) (int64, error) { return a - b, nil },
|
||||
MUL: func(a, b int64) (int64, error) { return a * b, nil },
|
||||
DIV: idiv,
|
||||
MOD: imod,
|
||||
AND: func(a, b int64) (int64, error) { return a & b, nil },
|
||||
OR: func(a, b int64) (int64, error) { return a | b, nil },
|
||||
XOR: func(a, b int64) (int64, error) { return a ^ b, nil },
|
||||
SHL: func(a, b int64) (int64, error) { return a << b, nil },
|
||||
SHR: func(a, b int64) (int64, error) { return a >> b, nil },
|
||||
POW: ipow,
|
||||
}
|
||||
|
||||
func (self *Expr) eval() (int64, error) {
|
||||
var lhs int64
|
||||
var rhs int64
|
||||
var err error
|
||||
var vfn func(int64, int64) (int64, error)
|
||||
|
||||
/* evaluate LHS */
|
||||
if lhs, err = self.Left.Evaluate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
/* check for unary operators */
|
||||
switch self.Op {
|
||||
case NOT: return self.unaryNot(lhs)
|
||||
case NEG: return self.unaryNeg(lhs)
|
||||
}
|
||||
|
||||
/* check for operators */
|
||||
if vfn = binaryEvaluators[self.Op]; vfn == nil {
|
||||
panic("invalid operator: " + self.Op.String())
|
||||
}
|
||||
|
||||
/* must be a binary expression */
|
||||
if self.Right == nil {
|
||||
panic("operator " + self.Op.String() + " is a binary operator")
|
||||
}
|
||||
|
||||
/* evaluate RHS, and call the operator */
|
||||
if rhs, err = self.Right.Evaluate(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return vfn(lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Expr) unaryNot(v int64) (int64, error) {
|
||||
if self.Right == nil {
|
||||
return ^v, nil
|
||||
} else {
|
||||
panic("operator Invert is an unary operator")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Expr) unaryNeg(v int64) (int64, error) {
|
||||
if self.Right == nil {
|
||||
return -v, nil
|
||||
} else {
|
||||
panic("operator Negate is an unary operator")
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
// SyntaxError represents a syntax error in the expression.
|
||||
type SyntaxError struct {
|
||||
Pos int
|
||||
Reason string
|
||||
}
|
||||
|
||||
func newSyntaxError(pos int, reason string) *SyntaxError {
|
||||
return &SyntaxError {
|
||||
Pos : pos,
|
||||
Reason : reason,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SyntaxError) Error() string {
|
||||
return fmt.Sprintf("Syntax error at position %d: %s", self.Pos, self.Reason)
|
||||
}
|
||||
|
||||
// RuntimeError is an error which would occure at run time.
|
||||
type RuntimeError struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
func newRuntimeError(reason string) *RuntimeError {
|
||||
return &RuntimeError {
|
||||
Reason: reason,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RuntimeError) Error() string {
|
||||
return "Runtime error: " + self.Reason
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
func idiv(v int64, d int64) (int64, error) {
|
||||
if d != 0 {
|
||||
return v / d, nil
|
||||
} else {
|
||||
return 0, newRuntimeError("division by zero")
|
||||
}
|
||||
}
|
||||
|
||||
func imod(v int64, d int64) (int64, error) {
|
||||
if d != 0 {
|
||||
return v % d, nil
|
||||
} else {
|
||||
return 0, newRuntimeError("division by zero")
|
||||
}
|
||||
}
|
||||
|
||||
func ipow(v int64, e int64) (int64, error) {
|
||||
mul := v
|
||||
ret := int64(1)
|
||||
|
||||
/* value must be 0 or positive */
|
||||
if v < 0 {
|
||||
return 0, newRuntimeError(fmt.Sprintf("negative base value: %d", v))
|
||||
}
|
||||
|
||||
/* exponent must be non-negative */
|
||||
if e < 0 {
|
||||
return 0, newRuntimeError(fmt.Sprintf("negative exponent: %d", e))
|
||||
}
|
||||
|
||||
/* fast power first round */
|
||||
if (e & 1) != 0 {
|
||||
ret *= mul
|
||||
}
|
||||
|
||||
/* fast power remaining rounds */
|
||||
for e >>= 1; e != 0; e >>= 1 {
|
||||
if mul *= mul; (e & 1) != 0 {
|
||||
ret *= mul
|
||||
}
|
||||
}
|
||||
|
||||
/* all done */
|
||||
return ret, nil
|
||||
}
|
@ -0,0 +1,313 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
`strconv`
|
||||
`unicode`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
type _TokenKind uint8
|
||||
|
||||
const (
|
||||
_T_end _TokenKind = iota + 1
|
||||
_T_int
|
||||
_T_punc
|
||||
_T_name
|
||||
)
|
||||
|
||||
const (
|
||||
_OP2 = 0x80
|
||||
_POW = _OP2 | '*'
|
||||
_SHL = _OP2 | '<'
|
||||
_SHR = _OP2 | '>'
|
||||
)
|
||||
|
||||
type _Slice struct {
|
||||
p unsafe.Pointer
|
||||
n int
|
||||
c int
|
||||
}
|
||||
|
||||
type _Token struct {
|
||||
pos int
|
||||
ptr *rune
|
||||
u64 uint64
|
||||
tag _TokenKind
|
||||
}
|
||||
|
||||
func (self _Token) str() (v string) {
|
||||
return string(self.rbuf())
|
||||
}
|
||||
|
||||
func (self _Token) rbuf() (v []rune) {
|
||||
(*_Slice)(unsafe.Pointer(&v)).c = int(self.u64)
|
||||
(*_Slice)(unsafe.Pointer(&v)).n = int(self.u64)
|
||||
(*_Slice)(unsafe.Pointer(&v)).p = unsafe.Pointer(self.ptr)
|
||||
return
|
||||
}
|
||||
|
||||
func tokenEnd(p int) _Token {
|
||||
return _Token {
|
||||
pos: p,
|
||||
tag: _T_end,
|
||||
}
|
||||
}
|
||||
|
||||
func tokenInt(p int, v uint64) _Token {
|
||||
return _Token {
|
||||
pos: p,
|
||||
u64: v,
|
||||
tag: _T_int,
|
||||
}
|
||||
}
|
||||
|
||||
func tokenPunc(p int, v rune) _Token {
|
||||
return _Token {
|
||||
pos: p,
|
||||
tag: _T_punc,
|
||||
u64: uint64(v),
|
||||
}
|
||||
}
|
||||
|
||||
func tokenName(p int, v []rune) _Token {
|
||||
return _Token {
|
||||
pos: p,
|
||||
ptr: &v[0],
|
||||
tag: _T_name,
|
||||
u64: uint64(len(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// Repository represents a repository of Term's.
|
||||
type Repository interface {
|
||||
Get(name string) (Term, error)
|
||||
}
|
||||
|
||||
// Parser parses an expression string to it's AST representation.
|
||||
type Parser struct {
|
||||
pos int
|
||||
src []rune
|
||||
}
|
||||
|
||||
var binaryOps = [...]func(*Expr, *Expr) *Expr {
|
||||
'+' : (*Expr).Add,
|
||||
'-' : (*Expr).Sub,
|
||||
'*' : (*Expr).Mul,
|
||||
'/' : (*Expr).Div,
|
||||
'%' : (*Expr).Mod,
|
||||
'&' : (*Expr).And,
|
||||
'^' : (*Expr).Xor,
|
||||
'|' : (*Expr).Or,
|
||||
_SHL : (*Expr).Shl,
|
||||
_SHR : (*Expr).Shr,
|
||||
_POW : (*Expr).Pow,
|
||||
}
|
||||
|
||||
var precedence = [...]map[int]bool {
|
||||
{_SHL: true, _SHR: true},
|
||||
{'|' : true},
|
||||
{'^' : true},
|
||||
{'&' : true},
|
||||
{'+' : true, '-': true},
|
||||
{'*' : true, '/': true, '%': true},
|
||||
{_POW: true},
|
||||
}
|
||||
|
||||
func (self *Parser) ch() rune {
|
||||
return self.src[self.pos]
|
||||
}
|
||||
|
||||
func (self *Parser) eof() bool {
|
||||
return self.pos >= len(self.src)
|
||||
}
|
||||
|
||||
func (self *Parser) rch() (v rune) {
|
||||
v, self.pos = self.src[self.pos], self.pos + 1
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Parser) hex(ss []rune) bool {
|
||||
if len(ss) == 1 && ss[0] == '0' {
|
||||
return unicode.ToLower(self.ch()) == 'x'
|
||||
} else if len(ss) <= 1 || unicode.ToLower(ss[1]) != 'x' {
|
||||
return unicode.IsDigit(self.ch())
|
||||
} else {
|
||||
return ishexdigit(self.ch())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) int(p int, ss []rune) (_Token, error) {
|
||||
var err error
|
||||
var val uint64
|
||||
|
||||
/* find all the digits */
|
||||
for !self.eof() && self.hex(ss) {
|
||||
ss = append(ss, self.rch())
|
||||
}
|
||||
|
||||
/* parse the value */
|
||||
if val, err = strconv.ParseUint(string(ss), 0, 64); err != nil {
|
||||
return _Token{}, err
|
||||
} else {
|
||||
return tokenInt(p, val), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) name(p int, ss []rune) _Token {
|
||||
for !self.eof() && isident(self.ch()) { ss = append(ss, self.rch()) }
|
||||
return tokenName(p, ss)
|
||||
}
|
||||
|
||||
func (self *Parser) read(p int, ch rune) (_Token, error) {
|
||||
if isdigit(ch) {
|
||||
return self.int(p, []rune { ch })
|
||||
} else if isident0(ch) {
|
||||
return self.name(p, []rune { ch }), nil
|
||||
} else if isop2ch(ch) && !self.eof() && self.ch() == ch {
|
||||
return tokenPunc(p, _OP2 | self.rch()), nil
|
||||
} else if isop1ch(ch) {
|
||||
return tokenPunc(p, ch), nil
|
||||
} else {
|
||||
return _Token{}, newSyntaxError(self.pos, "invalid character " + strconv.QuoteRuneToASCII(ch))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) next() (_Token, error) {
|
||||
for {
|
||||
var p int
|
||||
var c rune
|
||||
|
||||
/* check for EOF */
|
||||
if self.eof() {
|
||||
return tokenEnd(self.pos), nil
|
||||
}
|
||||
|
||||
/* read the next char */
|
||||
p = self.pos
|
||||
c = self.rch()
|
||||
|
||||
/* parse the token if not a space */
|
||||
if !unicode.IsSpace(c) {
|
||||
return self.read(p, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) grab(tk _Token, repo Repository) (*Expr, error) {
|
||||
if repo == nil {
|
||||
return nil, newSyntaxError(tk.pos, "unresolved symbol: " + tk.str())
|
||||
} else if term, err := repo.Get(tk.str()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return Ref(term), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) nest(nest int, repo Repository) (*Expr, error) {
|
||||
var err error
|
||||
var ret *Expr
|
||||
var ntk _Token
|
||||
|
||||
/* evaluate the nested expression */
|
||||
if ret, err = self.expr(0, nest + 1, repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* must follows with a ')' */
|
||||
if ntk, err = self.next(); err != nil {
|
||||
return nil, err
|
||||
} else if ntk.tag != _T_punc || ntk.u64 != ')' {
|
||||
return nil, newSyntaxError(ntk.pos, "')' expected")
|
||||
} else {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) unit(nest int, repo Repository) (*Expr, error) {
|
||||
if tk, err := self.next(); err != nil {
|
||||
return nil, err
|
||||
} else if tk.tag == _T_int {
|
||||
return Int(int64(tk.u64)), nil
|
||||
} else if tk.tag == _T_name {
|
||||
return self.grab(tk, repo)
|
||||
} else if tk.tag == _T_punc && tk.u64 == '(' {
|
||||
return self.nest(nest, repo)
|
||||
} else if tk.tag == _T_punc && tk.u64 == '+' {
|
||||
return self.unit(nest, repo)
|
||||
} else if tk.tag == _T_punc && tk.u64 == '-' {
|
||||
return neg2(self.unit(nest, repo))
|
||||
} else if tk.tag == _T_punc && tk.u64 == '~' {
|
||||
return not2(self.unit(nest, repo))
|
||||
} else {
|
||||
return nil, newSyntaxError(tk.pos, "integer, unary operator or nested expression expected")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) term(prec int, nest int, repo Repository) (*Expr, error) {
|
||||
var err error
|
||||
var val *Expr
|
||||
|
||||
/* parse the LHS operand */
|
||||
if val, err = self.expr(prec + 1, nest, repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* parse all the operators of the same precedence */
|
||||
for {
|
||||
var op int
|
||||
var rv *Expr
|
||||
var tk _Token
|
||||
|
||||
/* peek the next token */
|
||||
pp := self.pos
|
||||
tk, err = self.next()
|
||||
|
||||
/* check for errors */
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* encountered EOF */
|
||||
if tk.tag == _T_end {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
/* must be an operator */
|
||||
if tk.tag != _T_punc {
|
||||
return nil, newSyntaxError(tk.pos, "operators expected")
|
||||
}
|
||||
|
||||
/* check for the operator precedence */
|
||||
if op = int(tk.u64); !precedence[prec][op] {
|
||||
self.pos = pp
|
||||
return val, nil
|
||||
}
|
||||
|
||||
/* evaluate the RHS operand, and combine the value */
|
||||
if rv, err = self.expr(prec + 1, nest, repo); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
val = binaryOps[op](val, rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) expr(prec int, nest int, repo Repository) (*Expr, error) {
|
||||
if prec >= len(precedence) {
|
||||
return self.unit(nest, repo)
|
||||
} else {
|
||||
return self.term(prec, nest, repo)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses the expression, and returns it's AST tree.
|
||||
func (self *Parser) Parse(repo Repository) (*Expr, error) {
|
||||
return self.expr(0, 0, repo)
|
||||
}
|
||||
|
||||
// SetSource resets the expression parser and sets the expression source.
|
||||
func (self *Parser) SetSource(src string) *Parser {
|
||||
self.pos = 0
|
||||
self.src = []rune(src)
|
||||
return self
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
`sync`
|
||||
)
|
||||
|
||||
var (
|
||||
expressionPool sync.Pool
|
||||
)
|
||||
|
||||
func newExpression() *Expr {
|
||||
if v := expressionPool.Get(); v == nil {
|
||||
return new(Expr)
|
||||
} else {
|
||||
return resetExpression(v.(*Expr))
|
||||
}
|
||||
}
|
||||
|
||||
func freeExpression(p *Expr) {
|
||||
expressionPool.Put(p)
|
||||
}
|
||||
|
||||
func resetExpression(p *Expr) *Expr {
|
||||
*p = Expr{}
|
||||
return p
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package expr
|
||||
|
||||
// Term represents a value that can Evaluate() into an integer.
|
||||
type Term interface {
|
||||
Free()
|
||||
Evaluate() (int64, error)
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package expr
|
||||
|
||||
var op1ch = [...]bool {
|
||||
'+': true,
|
||||
'-': true,
|
||||
'*': true,
|
||||
'/': true,
|
||||
'%': true,
|
||||
'&': true,
|
||||
'|': true,
|
||||
'^': true,
|
||||
'~': true,
|
||||
'(': true,
|
||||
')': true,
|
||||
}
|
||||
|
||||
var op2ch = [...]bool {
|
||||
'*': true,
|
||||
'<': true,
|
||||
'>': true,
|
||||
}
|
||||
|
||||
func neg2(v *Expr, err error) (*Expr, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return v.Neg(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func not2(v *Expr, err error) (*Expr, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return v.Not(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func isop1ch(ch rune) bool {
|
||||
return ch >= 0 && int(ch) < len(op1ch) && op1ch[ch]
|
||||
}
|
||||
|
||||
func isop2ch(ch rune) bool {
|
||||
return ch >= 0 && int(ch) < len(op2ch) && op2ch[ch]
|
||||
}
|
||||
|
||||
func isdigit(ch rune) bool {
|
||||
return ch >= '0' && ch <= '9'
|
||||
}
|
||||
|
||||
func isident(ch rune) bool {
|
||||
return isdigit(ch) || isident0(ch)
|
||||
}
|
||||
|
||||
func isident0(ch rune) bool {
|
||||
return (ch == '_') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
}
|
||||
|
||||
func ishexdigit(ch rune) bool {
|
||||
return isdigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
// ISA represents an extension to x86-64 instruction set.
|
||||
type ISA uint64
|
||||
|
||||
const (
|
||||
ISA_CPUID ISA = 1 << iota
|
||||
ISA_RDTSC
|
||||
ISA_RDTSCP
|
||||
ISA_CMOV
|
||||
ISA_MOVBE
|
||||
ISA_POPCNT
|
||||
ISA_LZCNT
|
||||
ISA_TBM
|
||||
ISA_BMI
|
||||
ISA_BMI2
|
||||
ISA_ADX
|
||||
ISA_MMX
|
||||
ISA_MMX_PLUS
|
||||
ISA_FEMMS
|
||||
ISA_3DNOW
|
||||
ISA_3DNOW_PLUS
|
||||
ISA_SSE
|
||||
ISA_SSE2
|
||||
ISA_SSE3
|
||||
ISA_SSSE3
|
||||
ISA_SSE4A
|
||||
ISA_SSE4_1
|
||||
ISA_SSE4_2
|
||||
ISA_FMA3
|
||||
ISA_FMA4
|
||||
ISA_XOP
|
||||
ISA_F16C
|
||||
ISA_AVX
|
||||
ISA_AVX2
|
||||
ISA_AVX512F
|
||||
ISA_AVX512BW
|
||||
ISA_AVX512DQ
|
||||
ISA_AVX512VL
|
||||
ISA_AVX512PF
|
||||
ISA_AVX512ER
|
||||
ISA_AVX512CD
|
||||
ISA_AVX512VBMI
|
||||
ISA_AVX512IFMA
|
||||
ISA_AVX512VPOPCNTDQ
|
||||
ISA_AVX512_4VNNIW
|
||||
ISA_AVX512_4FMAPS
|
||||
ISA_PREFETCH
|
||||
ISA_PREFETCHW
|
||||
ISA_PREFETCHWT1
|
||||
ISA_CLFLUSH
|
||||
ISA_CLFLUSHOPT
|
||||
ISA_CLWB
|
||||
ISA_CLZERO
|
||||
ISA_RDRAND
|
||||
ISA_RDSEED
|
||||
ISA_PCLMULQDQ
|
||||
ISA_AES
|
||||
ISA_SHA
|
||||
ISA_MONITOR
|
||||
ISA_MONITORX
|
||||
ISA_ALL = ^ISA(0)
|
||||
)
|
||||
|
||||
var _ISA_NAMES = map[ISA]string {
|
||||
ISA_CPUID : "CPUID",
|
||||
ISA_RDTSC : "RDTSC",
|
||||
ISA_RDTSCP : "RDTSCP",
|
||||
ISA_CMOV : "CMOV",
|
||||
ISA_MOVBE : "MOVBE",
|
||||
ISA_POPCNT : "POPCNT",
|
||||
ISA_LZCNT : "LZCNT",
|
||||
ISA_TBM : "TBM",
|
||||
ISA_BMI : "BMI",
|
||||
ISA_BMI2 : "BMI2",
|
||||
ISA_ADX : "ADX",
|
||||
ISA_MMX : "MMX",
|
||||
ISA_MMX_PLUS : "MMX+",
|
||||
ISA_FEMMS : "FEMMS",
|
||||
ISA_3DNOW : "3dnow!",
|
||||
ISA_3DNOW_PLUS : "3dnow!+",
|
||||
ISA_SSE : "SSE",
|
||||
ISA_SSE2 : "SSE2",
|
||||
ISA_SSE3 : "SSE3",
|
||||
ISA_SSSE3 : "SSSE3",
|
||||
ISA_SSE4A : "SSE4A",
|
||||
ISA_SSE4_1 : "SSE4.1",
|
||||
ISA_SSE4_2 : "SSE4.2",
|
||||
ISA_FMA3 : "FMA3",
|
||||
ISA_FMA4 : "FMA4",
|
||||
ISA_XOP : "XOP",
|
||||
ISA_F16C : "F16C",
|
||||
ISA_AVX : "AVX",
|
||||
ISA_AVX2 : "AVX2",
|
||||
ISA_AVX512F : "AVX512F",
|
||||
ISA_AVX512BW : "AVX512BW",
|
||||
ISA_AVX512DQ : "AVX512DQ",
|
||||
ISA_AVX512VL : "AVX512VL",
|
||||
ISA_AVX512PF : "AVX512PF",
|
||||
ISA_AVX512ER : "AVX512ER",
|
||||
ISA_AVX512CD : "AVX512CD",
|
||||
ISA_AVX512VBMI : "AVX512VBMI",
|
||||
ISA_AVX512IFMA : "AVX512IFMA",
|
||||
ISA_AVX512VPOPCNTDQ : "AVX512VPOPCNTDQ",
|
||||
ISA_AVX512_4VNNIW : "AVX512_4VNNIW",
|
||||
ISA_AVX512_4FMAPS : "AVX512_4FMAPS",
|
||||
ISA_PREFETCH : "PREFETCH",
|
||||
ISA_PREFETCHW : "PREFETCHW",
|
||||
ISA_PREFETCHWT1 : "PREFETCHWT1",
|
||||
ISA_CLFLUSH : "CLFLUSH",
|
||||
ISA_CLFLUSHOPT : "CLFLUSHOPT",
|
||||
ISA_CLWB : "CLWB",
|
||||
ISA_CLZERO : "CLZERO",
|
||||
ISA_RDRAND : "RDRAND",
|
||||
ISA_RDSEED : "RDSEED",
|
||||
ISA_PCLMULQDQ : "PCLMULQDQ",
|
||||
ISA_AES : "AES",
|
||||
ISA_SHA : "SHA",
|
||||
ISA_MONITOR : "MONITOR",
|
||||
ISA_MONITORX : "MONITORX",
|
||||
}
|
||||
|
||||
var _ISA_MAPPING = map[string]ISA {
|
||||
"CPUID" : ISA_CPUID,
|
||||
"RDTSC" : ISA_RDTSC,
|
||||
"RDTSCP" : ISA_RDTSCP,
|
||||
"CMOV" : ISA_CMOV,
|
||||
"MOVBE" : ISA_MOVBE,
|
||||
"POPCNT" : ISA_POPCNT,
|
||||
"LZCNT" : ISA_LZCNT,
|
||||
"TBM" : ISA_TBM,
|
||||
"BMI" : ISA_BMI,
|
||||
"BMI2" : ISA_BMI2,
|
||||
"ADX" : ISA_ADX,
|
||||
"MMX" : ISA_MMX,
|
||||
"MMX+" : ISA_MMX_PLUS,
|
||||
"FEMMS" : ISA_FEMMS,
|
||||
"3dnow!" : ISA_3DNOW,
|
||||
"3dnow!+" : ISA_3DNOW_PLUS,
|
||||
"SSE" : ISA_SSE,
|
||||
"SSE2" : ISA_SSE2,
|
||||
"SSE3" : ISA_SSE3,
|
||||
"SSSE3" : ISA_SSSE3,
|
||||
"SSE4A" : ISA_SSE4A,
|
||||
"SSE4.1" : ISA_SSE4_1,
|
||||
"SSE4.2" : ISA_SSE4_2,
|
||||
"FMA3" : ISA_FMA3,
|
||||
"FMA4" : ISA_FMA4,
|
||||
"XOP" : ISA_XOP,
|
||||
"F16C" : ISA_F16C,
|
||||
"AVX" : ISA_AVX,
|
||||
"AVX2" : ISA_AVX2,
|
||||
"AVX512F" : ISA_AVX512F,
|
||||
"AVX512BW" : ISA_AVX512BW,
|
||||
"AVX512DQ" : ISA_AVX512DQ,
|
||||
"AVX512VL" : ISA_AVX512VL,
|
||||
"AVX512PF" : ISA_AVX512PF,
|
||||
"AVX512ER" : ISA_AVX512ER,
|
||||
"AVX512CD" : ISA_AVX512CD,
|
||||
"AVX512VBMI" : ISA_AVX512VBMI,
|
||||
"AVX512IFMA" : ISA_AVX512IFMA,
|
||||
"AVX512VPOPCNTDQ" : ISA_AVX512VPOPCNTDQ,
|
||||
"AVX512_4VNNIW" : ISA_AVX512_4VNNIW,
|
||||
"AVX512_4FMAPS" : ISA_AVX512_4FMAPS,
|
||||
"PREFETCH" : ISA_PREFETCH,
|
||||
"PREFETCHW" : ISA_PREFETCHW,
|
||||
"PREFETCHWT1" : ISA_PREFETCHWT1,
|
||||
"CLFLUSH" : ISA_CLFLUSH,
|
||||
"CLFLUSHOPT" : ISA_CLFLUSHOPT,
|
||||
"CLWB" : ISA_CLWB,
|
||||
"CLZERO" : ISA_CLZERO,
|
||||
"RDRAND" : ISA_RDRAND,
|
||||
"RDSEED" : ISA_RDSEED,
|
||||
"PCLMULQDQ" : ISA_PCLMULQDQ,
|
||||
"AES" : ISA_AES,
|
||||
"SHA" : ISA_SHA,
|
||||
"MONITOR" : ISA_MONITOR,
|
||||
"MONITORX" : ISA_MONITORX,
|
||||
}
|
||||
|
||||
func (self ISA) String() string {
|
||||
if v, ok := _ISA_NAMES[self]; ok {
|
||||
return v
|
||||
} else {
|
||||
return fmt.Sprintf("(invalid: %#x)", uint64(self))
|
||||
}
|
||||
}
|
||||
|
||||
// ParseISA parses name into ISA, it will panic if the name is invalid.
|
||||
func ParseISA(name string) ISA {
|
||||
if v, ok := _ISA_MAPPING[name]; ok {
|
||||
return v
|
||||
} else {
|
||||
panic("invalid ISA name: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
// Arch represents the x86_64 architecture.
|
||||
type Arch struct {
|
||||
isa ISA
|
||||
}
|
||||
|
||||
// DefaultArch is the default architecture with all ISA enabled.
|
||||
var DefaultArch = CreateArch()
|
||||
|
||||
// CreateArch creates a new Arch with all ISA enabled.
|
||||
func CreateArch() *Arch {
|
||||
return new(Arch).EnableISA(ISA_ALL)
|
||||
}
|
||||
|
||||
// HasISA checks if a particular ISA was enabled.
|
||||
func (self *Arch) HasISA(isa ISA) bool {
|
||||
return (self.isa & isa) != 0
|
||||
}
|
||||
|
||||
// EnableISA enables a particular ISA.
|
||||
func (self *Arch) EnableISA(isa ISA) *Arch {
|
||||
self.isa |= isa
|
||||
return self
|
||||
}
|
||||
|
||||
// DisableISA disables a particular ISA.
|
||||
func (self *Arch) DisableISA(isa ISA) *Arch {
|
||||
self.isa &^= isa
|
||||
return self
|
||||
}
|
||||
|
||||
// CreateProgram creates a new empty program.
|
||||
func (self *Arch) CreateProgram() *Program {
|
||||
return newProgram(self)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,33 @@
|
||||
package x86_64
|
||||
|
||||
func alias_INT3(p *Program, vv ...interface{}) *Instruction {
|
||||
if len(vv) == 0 {
|
||||
return p.INT(3)
|
||||
} else {
|
||||
panic("instruction INT3 takes no operands")
|
||||
}
|
||||
}
|
||||
|
||||
func alias_VCMPEQPS(p *Program, vv ...interface{}) *Instruction {
|
||||
if len(vv) >= 3 {
|
||||
return p.VCMPPS(0x00, vv[0], vv[1], vv[2], vv[3:]...)
|
||||
} else {
|
||||
panic("instruction VCMPEQPS takes 3 or 4 operands")
|
||||
}
|
||||
}
|
||||
|
||||
func alias_VCMPTRUEPS(p *Program, vv ...interface{}) *Instruction {
|
||||
if len(vv) >= 3 {
|
||||
return p.VCMPPS(0x0f, vv[0], vv[1], vv[2], vv[3:]...)
|
||||
} else {
|
||||
panic("instruction VCMPTRUEPS takes 3 or 4 operands")
|
||||
}
|
||||
}
|
||||
|
||||
var _InstructionAliases = map[string]_InstructionEncoder {
|
||||
"int3" : alias_INT3,
|
||||
"retq" : Instructions["ret"],
|
||||
"movabsq" : Instructions["movq"],
|
||||
"vcmpeqps" : alias_VCMPEQPS,
|
||||
"vcmptrueps" : alias_VCMPTRUEPS,
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
type _GoType struct {
|
||||
size uintptr
|
||||
pdata uintptr
|
||||
hash uint32
|
||||
flags uint8
|
||||
align uint8
|
||||
falign uint8
|
||||
kflags uint8
|
||||
traits unsafe.Pointer
|
||||
gcdata *byte
|
||||
str int32
|
||||
ptrx int32
|
||||
}
|
||||
|
||||
const (
|
||||
_KindMask = (1 << 5) - 1
|
||||
)
|
||||
|
||||
func (self *_GoType) kind() reflect.Kind {
|
||||
return reflect.Kind(self.kflags & _KindMask)
|
||||
}
|
||||
|
||||
type _GoSlice struct {
|
||||
ptr unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
type _GoEface struct {
|
||||
vt *_GoType
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func (self *_GoEface) kind() reflect.Kind {
|
||||
if self.vt != nil {
|
||||
return self.vt.kind()
|
||||
} else {
|
||||
return reflect.Invalid
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_GoEface) toInt64() int64 {
|
||||
if self.vt.size == 8 {
|
||||
return *(*int64)(self.ptr)
|
||||
} else if self.vt.size == 4 {
|
||||
return int64(*(*int32)(self.ptr))
|
||||
} else if self.vt.size == 2 {
|
||||
return int64(*(*int16)(self.ptr))
|
||||
} else {
|
||||
return int64(*(*int8)(self.ptr))
|
||||
}
|
||||
}
|
||||
|
||||
func efaceOf(v interface{}) _GoEface {
|
||||
return *(*_GoEface)(unsafe.Pointer(&v))
|
||||
}
|
@ -0,0 +1,675 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`encoding/binary`
|
||||
`math`
|
||||
)
|
||||
|
||||
/** Operand Encoding Helpers **/
|
||||
|
||||
func imml(v interface{}) byte {
|
||||
return byte(toImmAny(v) & 0x0f)
|
||||
}
|
||||
|
||||
func relv(v interface{}) int64 {
|
||||
switch r := v.(type) {
|
||||
case *Label : return 0
|
||||
case RelativeOffset : return int64(r)
|
||||
default : panic("invalid relative offset")
|
||||
}
|
||||
}
|
||||
|
||||
func addr(v interface{}) interface{} {
|
||||
switch a := v.(*MemoryOperand).Addr; a.Type {
|
||||
case Memory : return a.Memory
|
||||
case Offset : return a.Offset
|
||||
case Reference : return a.Reference
|
||||
default : panic("invalid memory operand type")
|
||||
}
|
||||
}
|
||||
|
||||
func bcode(v interface{}) byte {
|
||||
if m, ok := v.(*MemoryOperand); !ok {
|
||||
panic("v is not a memory operand")
|
||||
} else if m.Broadcast == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func vcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case XMMRegister : return byte(r)
|
||||
case YMMRegister : return byte(r)
|
||||
case ZMMRegister : return byte(r)
|
||||
case MaskedRegister : return vcode(r.Reg)
|
||||
default : panic("v is not a vector register")
|
||||
}
|
||||
}
|
||||
|
||||
func kcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case KRegister : return byte(r)
|
||||
case XMMRegister : return 0
|
||||
case YMMRegister : return 0
|
||||
case ZMMRegister : return 0
|
||||
case RegisterMask : return byte(r.K)
|
||||
case MaskedRegister : return byte(r.Mask.K)
|
||||
case *MemoryOperand : return toKcodeMem(r)
|
||||
default : panic("v is not a maskable operand")
|
||||
}
|
||||
}
|
||||
|
||||
func zcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case KRegister : return 0
|
||||
case XMMRegister : return 0
|
||||
case YMMRegister : return 0
|
||||
case ZMMRegister : return 0
|
||||
case RegisterMask : return toZcodeRegM(r)
|
||||
case MaskedRegister : return toZcodeRegM(r.Mask)
|
||||
case *MemoryOperand : return toZcodeMem(r)
|
||||
default : panic("v is not a maskable operand")
|
||||
}
|
||||
}
|
||||
|
||||
func lcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case Register8 : return byte(r & 0x07)
|
||||
case Register16 : return byte(r & 0x07)
|
||||
case Register32 : return byte(r & 0x07)
|
||||
case Register64 : return byte(r & 0x07)
|
||||
case KRegister : return byte(r & 0x07)
|
||||
case MMRegister : return byte(r & 0x07)
|
||||
case XMMRegister : return byte(r & 0x07)
|
||||
case YMMRegister : return byte(r & 0x07)
|
||||
case ZMMRegister : return byte(r & 0x07)
|
||||
case MaskedRegister : return lcode(r.Reg)
|
||||
default : panic("v is not a register")
|
||||
}
|
||||
}
|
||||
|
||||
func hcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case Register8 : return byte(r >> 3) & 1
|
||||
case Register16 : return byte(r >> 3) & 1
|
||||
case Register32 : return byte(r >> 3) & 1
|
||||
case Register64 : return byte(r >> 3) & 1
|
||||
case KRegister : return byte(r >> 3) & 1
|
||||
case MMRegister : return byte(r >> 3) & 1
|
||||
case XMMRegister : return byte(r >> 3) & 1
|
||||
case YMMRegister : return byte(r >> 3) & 1
|
||||
case ZMMRegister : return byte(r >> 3) & 1
|
||||
case MaskedRegister : return hcode(r.Reg)
|
||||
default : panic("v is not a register")
|
||||
}
|
||||
}
|
||||
|
||||
func ecode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case Register8 : return byte(r >> 4) & 1
|
||||
case Register16 : return byte(r >> 4) & 1
|
||||
case Register32 : return byte(r >> 4) & 1
|
||||
case Register64 : return byte(r >> 4) & 1
|
||||
case KRegister : return byte(r >> 4) & 1
|
||||
case MMRegister : return byte(r >> 4) & 1
|
||||
case XMMRegister : return byte(r >> 4) & 1
|
||||
case YMMRegister : return byte(r >> 4) & 1
|
||||
case ZMMRegister : return byte(r >> 4) & 1
|
||||
case MaskedRegister : return ecode(r.Reg)
|
||||
default : panic("v is not a register")
|
||||
}
|
||||
}
|
||||
|
||||
func hlcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case Register8 : return toHLcodeReg8(r)
|
||||
case Register16 : return byte(r & 0x0f)
|
||||
case Register32 : return byte(r & 0x0f)
|
||||
case Register64 : return byte(r & 0x0f)
|
||||
case KRegister : return byte(r & 0x0f)
|
||||
case MMRegister : return byte(r & 0x0f)
|
||||
case XMMRegister : return byte(r & 0x0f)
|
||||
case YMMRegister : return byte(r & 0x0f)
|
||||
case ZMMRegister : return byte(r & 0x0f)
|
||||
case MaskedRegister : return hlcode(r.Reg)
|
||||
default : panic("v is not a register")
|
||||
}
|
||||
}
|
||||
|
||||
func ehcode(v interface{}) byte {
|
||||
switch r := v.(type) {
|
||||
case Register8 : return byte(r >> 3) & 0x03
|
||||
case Register16 : return byte(r >> 3) & 0x03
|
||||
case Register32 : return byte(r >> 3) & 0x03
|
||||
case Register64 : return byte(r >> 3) & 0x03
|
||||
case KRegister : return byte(r >> 3) & 0x03
|
||||
case MMRegister : return byte(r >> 3) & 0x03
|
||||
case XMMRegister : return byte(r >> 3) & 0x03
|
||||
case YMMRegister : return byte(r >> 3) & 0x03
|
||||
case ZMMRegister : return byte(r >> 3) & 0x03
|
||||
case MaskedRegister : return ehcode(r.Reg)
|
||||
default : panic("v is not a register")
|
||||
}
|
||||
}
|
||||
|
||||
func toImmAny(v interface{}) int64 {
|
||||
if x, ok := asInt64(v); ok {
|
||||
return x
|
||||
} else {
|
||||
panic("value is not an integer")
|
||||
}
|
||||
}
|
||||
|
||||
func toHcodeOpt(v interface{}) byte {
|
||||
if v == nil {
|
||||
return 0
|
||||
} else {
|
||||
return hcode(v)
|
||||
}
|
||||
}
|
||||
|
||||
func toEcodeVMM(v interface{}, x byte) byte {
|
||||
switch r := v.(type) {
|
||||
case XMMRegister : return ecode(r)
|
||||
case YMMRegister : return ecode(r)
|
||||
case ZMMRegister : return ecode(r)
|
||||
default : return x
|
||||
}
|
||||
}
|
||||
|
||||
func toKcodeMem(v *MemoryOperand) byte {
|
||||
if !v.Masked {
|
||||
return 0
|
||||
} else {
|
||||
return byte(v.Mask.K)
|
||||
}
|
||||
}
|
||||
|
||||
func toZcodeMem(v *MemoryOperand) byte {
|
||||
if !v.Masked || v.Mask.Z {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func toZcodeRegM(v RegisterMask) byte {
|
||||
if v.Z {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func toHLcodeReg8(v Register8) byte {
|
||||
switch v {
|
||||
case AH: fallthrough
|
||||
case BH: fallthrough
|
||||
case CH: fallthrough
|
||||
case DH: panic("ah/bh/ch/dh registers never use 4-bit encoding")
|
||||
default: return byte(v & 0x0f)
|
||||
}
|
||||
}
|
||||
|
||||
/** Instruction Encoding Helpers **/
|
||||
|
||||
const (
|
||||
_N_inst = 16
|
||||
)
|
||||
|
||||
const (
|
||||
_F_rel1 = 1 << iota
|
||||
_F_rel4
|
||||
)
|
||||
|
||||
type _Encoding struct {
|
||||
len int
|
||||
flags int
|
||||
bytes [_N_inst]byte
|
||||
encoder func(m *_Encoding, v []interface{})
|
||||
}
|
||||
|
||||
// buf ensures len + n <= len(bytes).
|
||||
func (self *_Encoding) buf(n int) []byte {
|
||||
if i := self.len; i + n > _N_inst {
|
||||
panic("instruction too long")
|
||||
} else {
|
||||
return self.bytes[i:]
|
||||
}
|
||||
}
|
||||
|
||||
// emit encodes a single byte.
|
||||
func (self *_Encoding) emit(v byte) {
|
||||
self.buf(1)[0] = v
|
||||
self.len++
|
||||
}
|
||||
|
||||
// imm1 encodes a single byte immediate value.
|
||||
func (self *_Encoding) imm1(v int64) {
|
||||
self.emit(byte(v))
|
||||
}
|
||||
|
||||
// imm2 encodes a two-byte immediate value in little-endian.
|
||||
func (self *_Encoding) imm2(v int64) {
|
||||
binary.LittleEndian.PutUint16(self.buf(2), uint16(v))
|
||||
self.len += 2
|
||||
}
|
||||
|
||||
// imm4 encodes a 4-byte immediate value in little-endian.
|
||||
func (self *_Encoding) imm4(v int64) {
|
||||
binary.LittleEndian.PutUint32(self.buf(4), uint32(v))
|
||||
self.len += 4
|
||||
}
|
||||
|
||||
// imm8 encodes an 8-byte immediate value in little-endian.
|
||||
func (self *_Encoding) imm8(v int64) {
|
||||
binary.LittleEndian.PutUint64(self.buf(8), uint64(v))
|
||||
self.len += 8
|
||||
}
|
||||
|
||||
// vex2 encodes a 2-byte or 3-byte VEX prefix.
|
||||
//
|
||||
// 2-byte VEX prefix:
|
||||
// Requires: VEX.W = 0, VEX.mmmmm = 0b00001 and VEX.B = VEX.X = 0
|
||||
// +----------------+
|
||||
// Byte 0: | Bits 0-7: 0xc5 |
|
||||
// +----------------+
|
||||
//
|
||||
// +-----------+----------------+----------+--------------+
|
||||
// Byte 1: | Bit 7: ~R | Bits 3-6 ~vvvv | Bit 2: L | Bits 0-1: pp |
|
||||
// +-----------+----------------+----------+--------------+
|
||||
//
|
||||
// 3-byte VEX prefix:
|
||||
// +----------------+
|
||||
// Byte 0: | Bits 0-7: 0xc4 |
|
||||
// +----------------+
|
||||
//
|
||||
// +-----------+-----------+-----------+-------------------+
|
||||
// Byte 1: | Bit 7: ~R | Bit 6: ~X | Bit 5: ~B | Bits 0-4: 0b00001 |
|
||||
// +-----------+-----------+-----------+-------------------+
|
||||
//
|
||||
// +----------+-----------------+----------+--------------+
|
||||
// Byte 2: | Bit 7: 0 | Bits 3-6: ~vvvv | Bit 2: L | Bits 0-1: pp |
|
||||
// +----------+-----------------+----------+--------------+
|
||||
//
|
||||
func (self *_Encoding) vex2(lpp byte, r byte, rm interface{}, vvvv byte) {
|
||||
var b byte
|
||||
var x byte
|
||||
|
||||
/* VEX.R must be a single-bit mask */
|
||||
if r > 1 {
|
||||
panic("VEX.R must be a 1-bit mask")
|
||||
}
|
||||
|
||||
/* VEX.Lpp must be a 3-bit mask */
|
||||
if lpp &^ 0b111 != 0 {
|
||||
panic("VEX.Lpp must be a 3-bit mask")
|
||||
}
|
||||
|
||||
/* VEX.vvvv must be a 4-bit mask */
|
||||
if vvvv &^ 0b1111 != 0 {
|
||||
panic("VEX.vvvv must be a 4-bit mask")
|
||||
}
|
||||
|
||||
/* encode the RM bits if any */
|
||||
if rm != nil {
|
||||
switch v := rm.(type) {
|
||||
case *Label : break
|
||||
case Register : b = hcode(v)
|
||||
case MemoryAddress : b, x = toHcodeOpt(v.Base), toHcodeOpt(v.Index)
|
||||
case RelativeOffset : break
|
||||
default : panic("rm is expected to be a register or a memory address")
|
||||
}
|
||||
}
|
||||
|
||||
/* if VEX.B and VEX.X are zeroes, 2-byte VEX prefix can be used */
|
||||
if x == 0 && b == 0 {
|
||||
self.emit(0xc5)
|
||||
self.emit(0xf8 ^ (r << 7) ^ (vvvv << 3) ^ lpp)
|
||||
} else {
|
||||
self.emit(0xc4)
|
||||
self.emit(0xe1 ^ (r << 7) ^ (x << 6) ^ (b << 5))
|
||||
self.emit(0x78 ^ (vvvv << 3) ^ lpp)
|
||||
}
|
||||
}
|
||||
|
||||
// vex3 encodes a 3-byte VEX or XOP prefix.
|
||||
//
|
||||
// 3-byte VEX/XOP prefix
|
||||
// +-----------------------------------+
|
||||
// Byte 0: | Bits 0-7: 0xc4 (VEX) / 0x8f (XOP) |
|
||||
// +-----------------------------------+
|
||||
//
|
||||
// +-----------+-----------+-----------+-----------------+
|
||||
// Byte 1: | Bit 7: ~R | Bit 6: ~X | Bit 5: ~B | Bits 0-4: mmmmm |
|
||||
// +-----------+-----------+-----------+-----------------+
|
||||
//
|
||||
// +----------+-----------------+----------+--------------+
|
||||
// Byte 2: | Bit 7: W | Bits 3-6: ~vvvv | Bit 2: L | Bits 0-1: pp |
|
||||
// +----------+-----------------+----------+--------------+
|
||||
//
|
||||
func (self *_Encoding) vex3(esc byte, mmmmm byte, wlpp byte, r byte, rm interface{}, vvvv byte) {
|
||||
var b byte
|
||||
var x byte
|
||||
|
||||
/* VEX.R must be a single-bit mask */
|
||||
if r > 1 {
|
||||
panic("VEX.R must be a 1-bit mask")
|
||||
}
|
||||
|
||||
/* VEX.vvvv must be a 4-bit mask */
|
||||
if vvvv &^ 0b1111 != 0 {
|
||||
panic("VEX.vvvv must be a 4-bit mask")
|
||||
}
|
||||
|
||||
/* escape must be a 3-byte VEX (0xc4) or XOP (0x8f) prefix */
|
||||
if esc != 0xc4 && esc != 0x8f {
|
||||
panic("escape must be a 3-byte VEX (0xc4) or XOP (0x8f) prefix")
|
||||
}
|
||||
|
||||
/* VEX.W____Lpp is expected to have no bits set except 0, 1, 2 and 7 */
|
||||
if wlpp &^ 0b10000111 != 0 {
|
||||
panic("VEX.W____Lpp is expected to have no bits set except 0, 1, 2 and 7")
|
||||
}
|
||||
|
||||
/* VEX.m-mmmm is expected to be a 5-bit mask */
|
||||
if mmmmm &^ 0b11111 != 0 {
|
||||
panic("VEX.m-mmmm is expected to be a 5-bit mask")
|
||||
}
|
||||
|
||||
/* encode the RM bits */
|
||||
switch v := rm.(type) {
|
||||
case *Label : break
|
||||
case MemoryAddress : b, x = toHcodeOpt(v.Base), toHcodeOpt(v.Index)
|
||||
case RelativeOffset : break
|
||||
default : panic("rm is expected to be a register or a memory address")
|
||||
}
|
||||
|
||||
/* encode the 3-byte VEX or XOP prefix */
|
||||
self.emit(esc)
|
||||
self.emit(0xe0 ^ (r << 7) ^ (x << 6) ^ (b << 5) ^ mmmmm)
|
||||
self.emit(0x78 ^ (vvvv << 3) ^ wlpp)
|
||||
}
|
||||
|
||||
// evex encodes a 4-byte EVEX prefix.
|
||||
func (self *_Encoding) evex(mm byte, w1pp byte, ll byte, rr byte, rm interface{}, vvvvv byte, aaa byte, zz byte, bb byte) {
|
||||
var b byte
|
||||
var x byte
|
||||
|
||||
/* EVEX.b must be a single-bit mask */
|
||||
if bb > 1 {
|
||||
panic("EVEX.b must be a 1-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.z must be a single-bit mask */
|
||||
if zz > 1 {
|
||||
panic("EVEX.z must be a 1-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.mm must be a 2-bit mask */
|
||||
if mm &^ 0b11 != 0 {
|
||||
panic("EVEX.mm must be a 2-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.L'L must be a 2-bit mask */
|
||||
if ll &^ 0b11 != 0 {
|
||||
panic("EVEX.L'L must be a 2-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.R'R must be a 2-bit mask */
|
||||
if rr &^ 0b11 != 0 {
|
||||
panic("EVEX.R'R must be a 2-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.aaa must be a 3-bit mask */
|
||||
if aaa &^ 0b111 != 0 {
|
||||
panic("EVEX.aaa must be a 3-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.v'vvvv must be a 5-bit mask */
|
||||
if vvvvv &^ 0b11111 != 0 {
|
||||
panic("EVEX.v'vvvv must be a 5-bit mask")
|
||||
}
|
||||
|
||||
/* EVEX.W____1pp is expected to have no bits set except 0, 1, 2, and 7 */
|
||||
if w1pp &^ 0b10000011 != 0b100 {
|
||||
panic("EVEX.W____1pp is expected to have no bits set except 0, 1, 2, and 7")
|
||||
}
|
||||
|
||||
/* extract bits from EVEX.R'R and EVEX.v'vvvv */
|
||||
r1, r0 := rr >> 1, rr & 1
|
||||
v1, v0 := vvvvv >> 4, vvvvv & 0b1111
|
||||
|
||||
/* encode the RM bits if any */
|
||||
if rm != nil {
|
||||
switch m := rm.(type) {
|
||||
case *Label : break
|
||||
case Register : b, x = hcode(m), ecode(m)
|
||||
case MemoryAddress : b, x, v1 = toHcodeOpt(m.Base), toHcodeOpt(m.Index), toEcodeVMM(m.Index, v1)
|
||||
case RelativeOffset : break
|
||||
default : panic("rm is expected to be a register or a memory address")
|
||||
}
|
||||
}
|
||||
|
||||
/* EVEX prefix bytes */
|
||||
p0 := (r0 << 7) | (x << 6) | (b << 5) | (r1 << 4) | mm
|
||||
p1 := (v0 << 3) | w1pp
|
||||
p2 := (zz << 7) | (ll << 5) | (b << 4) | (v1 << 3) | aaa
|
||||
|
||||
/* p0: invert RXBR' (bits 4-7)
|
||||
* p1: invert vvvv (bits 3-6)
|
||||
* p2: invert V' (bit 3) */
|
||||
self.emit(0x62)
|
||||
self.emit(p0 ^ 0xf0)
|
||||
self.emit(p1 ^ 0x78)
|
||||
self.emit(p2 ^ 0x08)
|
||||
}
|
||||
|
||||
// rexm encodes a mandatory REX prefix.
|
||||
func (self *_Encoding) rexm(w byte, r byte, rm interface{}) {
|
||||
var b byte
|
||||
var x byte
|
||||
|
||||
/* REX.R must be 0 or 1 */
|
||||
if r != 0 && r != 1 {
|
||||
panic("REX.R must be 0 or 1")
|
||||
}
|
||||
|
||||
/* REX.W must be 0 or 1 */
|
||||
if w != 0 && w != 1 {
|
||||
panic("REX.W must be 0 or 1")
|
||||
}
|
||||
|
||||
/* encode the RM bits */
|
||||
switch v := rm.(type) {
|
||||
case *Label : break
|
||||
case MemoryAddress : b, x = toHcodeOpt(v.Base), toHcodeOpt(v.Index)
|
||||
case RelativeOffset : break
|
||||
default : panic("rm is expected to be a register or a memory address")
|
||||
}
|
||||
|
||||
/* encode the REX prefix */
|
||||
self.emit(0x40 | (w << 3) | (r << 2) | (x << 1) | b)
|
||||
}
|
||||
|
||||
// rexo encodes an optional REX prefix.
|
||||
func (self *_Encoding) rexo(r byte, rm interface{}, force bool) {
|
||||
var b byte
|
||||
var x byte
|
||||
|
||||
/* REX.R must be 0 or 1 */
|
||||
if r != 0 && r != 1 {
|
||||
panic("REX.R must be 0 or 1")
|
||||
}
|
||||
|
||||
/* encode the RM bits */
|
||||
switch v := rm.(type) {
|
||||
case *Label : break
|
||||
case Register : b = hcode(v)
|
||||
case MemoryAddress : b, x = toHcodeOpt(v.Base), toHcodeOpt(v.Index)
|
||||
case RelativeOffset : break
|
||||
default : panic("rm is expected to be a register or a memory address")
|
||||
}
|
||||
|
||||
/* if REX.R, REX.X, and REX.B are all zeroes, REX prefix can be omitted */
|
||||
if force || r != 0 || x != 0 || b != 0 {
|
||||
self.emit(0x40 | (r << 2) | (x << 1) | b)
|
||||
}
|
||||
}
|
||||
|
||||
// mrsd encodes ModR/M, SIB and Displacement.
|
||||
//
|
||||
// ModR/M byte
|
||||
// +----------------+---------------+---------------+
|
||||
// | Bits 6-7: Mode | Bits 3-5: Reg | Bits 0-2: R/M |
|
||||
// +----------------+---------------+---------------+
|
||||
//
|
||||
// SIB byte
|
||||
// +-----------------+-----------------+----------------+
|
||||
// | Bits 6-7: Scale | Bits 3-5: Index | Bits 0-2: Base |
|
||||
// +-----------------+-----------------+----------------+
|
||||
//
|
||||
func (self *_Encoding) mrsd(reg byte, rm interface{}, disp8v int32) {
|
||||
var ok bool
|
||||
var mm MemoryAddress
|
||||
var ro RelativeOffset
|
||||
|
||||
/* ModRM encodes the lower 3-bit of the register */
|
||||
if reg > 7 {
|
||||
panic("invalid register bits")
|
||||
}
|
||||
|
||||
/* check the displacement scale */
|
||||
switch disp8v {
|
||||
case 1: break
|
||||
case 2: break
|
||||
case 4: break
|
||||
case 8: break
|
||||
case 16: break
|
||||
case 32: break
|
||||
case 64: break
|
||||
default: panic("invalid displacement size")
|
||||
}
|
||||
|
||||
/* special case: unresolved labels, assuming a zero offset */
|
||||
if _, ok = rm.(*Label); ok {
|
||||
self.emit(0x05 | (reg << 3))
|
||||
self.imm4(0)
|
||||
return
|
||||
}
|
||||
|
||||
/* special case: RIP-relative offset
|
||||
* ModRM.Mode == 0 and ModeRM.R/M == 5 indicates (rip + disp32) addressing */
|
||||
if ro, ok = rm.(RelativeOffset); ok {
|
||||
self.emit(0x05 | (reg << 3))
|
||||
self.imm4(int64(ro))
|
||||
return
|
||||
}
|
||||
|
||||
/* must be a generic memory address */
|
||||
if mm, ok = rm.(MemoryAddress); !ok {
|
||||
panic("rm must be a memory address")
|
||||
}
|
||||
|
||||
/* absolute addressing, encoded as disp(%rbp,%rsp,1) */
|
||||
if mm.Base == nil && mm.Index == nil {
|
||||
self.emit(0x04 | (reg << 3))
|
||||
self.emit(0x25)
|
||||
self.imm4(int64(mm.Displacement))
|
||||
return
|
||||
}
|
||||
|
||||
/* no SIB byte */
|
||||
if mm.Index == nil && lcode(mm.Base) != 0b100 {
|
||||
cc := lcode(mm.Base)
|
||||
dv := mm.Displacement
|
||||
|
||||
/* ModRM.Mode == 0 (no displacement) */
|
||||
if dv == 0 && mm.Base != RBP && mm.Base != R13 {
|
||||
if cc == 0b101 {
|
||||
panic("rbp/r13 is not encodable as a base register (interpreted as disp32 address)")
|
||||
} else {
|
||||
self.emit((reg << 3) | cc)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* ModRM.Mode == 1 (8-bit displacement) */
|
||||
if dq := dv / disp8v; dq >= math.MinInt8 && dq <= math.MaxInt8 && dv % disp8v == 0 {
|
||||
self.emit(0x40 | (reg << 3) | cc)
|
||||
self.imm1(int64(dq))
|
||||
return
|
||||
}
|
||||
|
||||
/* ModRM.Mode == 2 (32-bit displacement) */
|
||||
self.emit(0x80 | (reg << 3) | cc)
|
||||
self.imm4(int64(mm.Displacement))
|
||||
return
|
||||
}
|
||||
|
||||
/* all encodings below use ModRM.R/M = 4 (0b100) to indicate the presence of SIB */
|
||||
if mm.Index == RSP {
|
||||
panic("rsp is not encodable as an index register (interpreted as no index)")
|
||||
}
|
||||
|
||||
/* index = 4 (0b100) denotes no-index encoding */
|
||||
var scale byte
|
||||
var index byte = 0x04
|
||||
|
||||
/* encode the scale byte */
|
||||
if mm.Scale != 0 {
|
||||
switch mm.Scale {
|
||||
case 1 : scale = 0
|
||||
case 2 : scale = 1
|
||||
case 4 : scale = 2
|
||||
case 8 : scale = 3
|
||||
default : panic("invalid scale value")
|
||||
}
|
||||
}
|
||||
|
||||
/* encode the index byte */
|
||||
if mm.Index != nil {
|
||||
index = lcode(mm.Index)
|
||||
}
|
||||
|
||||
/* SIB.Base = 5 (0b101) and ModRM.Mode = 0 indicates no-base encoding with disp32 */
|
||||
if mm.Base == nil {
|
||||
self.emit((reg << 3) | 0b100)
|
||||
self.emit((scale << 6) | (index << 3) | 0b101)
|
||||
self.imm4(int64(mm.Displacement))
|
||||
return
|
||||
}
|
||||
|
||||
/* base L-code & displacement value */
|
||||
cc := lcode(mm.Base)
|
||||
dv := mm.Displacement
|
||||
|
||||
/* ModRM.Mode == 0 (no displacement) */
|
||||
if dv == 0 && cc != 0b101 {
|
||||
self.emit((reg << 3) | 0b100)
|
||||
self.emit((scale << 6) | (index << 3) | cc)
|
||||
return
|
||||
}
|
||||
|
||||
/* ModRM.Mode == 1 (8-bit displacement) */
|
||||
if dq := dv / disp8v; dq >= math.MinInt8 && dq <= math.MaxInt8 && dv % disp8v == 0 {
|
||||
self.emit(0x44 | (reg << 3))
|
||||
self.emit((scale << 6) | (index << 3) | cc)
|
||||
self.imm1(int64(dq))
|
||||
return
|
||||
}
|
||||
|
||||
/* ModRM.Mode == 2 (32-bit displacement) */
|
||||
self.emit(0x84 | (reg << 3))
|
||||
self.emit((scale << 6) | (index << 3) | cc)
|
||||
self.imm4(int64(mm.Displacement))
|
||||
}
|
||||
|
||||
// encode invokes the encoder to encode this instruction.
|
||||
func (self *_Encoding) encode(v []interface{}) int {
|
||||
self.len = 0
|
||||
self.encoder(self, v)
|
||||
return self.len
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,510 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`errors`
|
||||
`fmt`
|
||||
`math`
|
||||
`reflect`
|
||||
`strconv`
|
||||
`strings`
|
||||
`sync/atomic`
|
||||
)
|
||||
|
||||
// RelativeOffset represents an RIP-relative offset.
|
||||
type RelativeOffset int32
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self RelativeOffset) String() string {
|
||||
if self == 0 {
|
||||
return "(%rip)"
|
||||
} else {
|
||||
return fmt.Sprintf("%d(%%rip)", self)
|
||||
}
|
||||
}
|
||||
|
||||
// RoundingControl represents a floating-point rounding option.
|
||||
type RoundingControl uint8
|
||||
|
||||
const (
|
||||
// RN_SAE represents "Round Nearest", which is the default rounding option.
|
||||
RN_SAE RoundingControl = iota
|
||||
|
||||
// RD_SAE represents "Round Down".
|
||||
RD_SAE
|
||||
|
||||
// RU_SAE represents "Round Up".
|
||||
RU_SAE
|
||||
|
||||
// RZ_SAE represents "Round towards Zero".
|
||||
RZ_SAE
|
||||
)
|
||||
|
||||
var _RC_NAMES = map[RoundingControl]string {
|
||||
RN_SAE: "rn-sae",
|
||||
RD_SAE: "rd-sae",
|
||||
RU_SAE: "ru-sae",
|
||||
RZ_SAE: "rz-sae",
|
||||
}
|
||||
|
||||
func (self RoundingControl) String() string {
|
||||
if v, ok := _RC_NAMES[self]; ok {
|
||||
return v
|
||||
} else {
|
||||
panic("invalid RoundingControl value")
|
||||
}
|
||||
}
|
||||
|
||||
// ExceptionControl represents the "Suppress All Exceptions" flag.
|
||||
type ExceptionControl uint8
|
||||
|
||||
const (
|
||||
// SAE represents the flag "Suppress All Exceptions" for floating point operations.
|
||||
SAE ExceptionControl = iota
|
||||
)
|
||||
|
||||
func (ExceptionControl) String() string {
|
||||
return "sae"
|
||||
}
|
||||
|
||||
// AddressType indicates which kind of value that an Addressable object contains.
|
||||
type AddressType uint
|
||||
|
||||
const (
|
||||
// None indicates the Addressable does not contain any addressable value.
|
||||
None AddressType = iota
|
||||
|
||||
// Memory indicates the Addressable contains a memory address.
|
||||
Memory
|
||||
|
||||
// Offset indicates the Addressable contains an RIP-relative offset.
|
||||
Offset
|
||||
|
||||
// Reference indicates the Addressable contains a label reference.
|
||||
Reference
|
||||
)
|
||||
|
||||
// Disposable is a type of object that can be Free'd manually.
|
||||
type Disposable interface {
|
||||
Free()
|
||||
}
|
||||
|
||||
// Label represents a location within the program.
|
||||
type Label struct {
|
||||
refs int64
|
||||
Name string
|
||||
Dest *Instruction
|
||||
}
|
||||
|
||||
func (self *Label) offset(p uintptr, n int) RelativeOffset {
|
||||
if self.Dest == nil {
|
||||
panic("unresolved label: " + self.Name)
|
||||
} else {
|
||||
return RelativeOffset(self.Dest.pc - p - uintptr(n))
|
||||
}
|
||||
}
|
||||
|
||||
// Free decreases the reference count of a Label, if the
|
||||
// refcount drops to 0, the Label will be recycled.
|
||||
func (self *Label) Free() {
|
||||
if atomic.AddInt64(&self.refs, -1) == 0 {
|
||||
freeLabel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self *Label) String() string {
|
||||
if self.Dest == nil {
|
||||
return fmt.Sprintf("%s(%%rip)", self.Name)
|
||||
} else {
|
||||
return fmt.Sprintf("%s(%%rip)@%#x", self.Name, self.Dest.pc)
|
||||
}
|
||||
}
|
||||
|
||||
// Retain increases the reference count of a Label.
|
||||
func (self *Label) Retain() *Label {
|
||||
atomic.AddInt64(&self.refs, 1)
|
||||
return self
|
||||
}
|
||||
|
||||
// Evaluate implements the interface expr.Term.
|
||||
func (self *Label) Evaluate() (int64, error) {
|
||||
if self.Dest != nil {
|
||||
return int64(self.Dest.pc), nil
|
||||
} else {
|
||||
return 0, errors.New("unresolved label: " + self.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Addressable is a union to represent an addressable operand.
|
||||
type Addressable struct {
|
||||
Type AddressType
|
||||
Memory MemoryAddress
|
||||
Offset RelativeOffset
|
||||
Reference *Label
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self *Addressable) String() string {
|
||||
switch self.Type {
|
||||
case None : return "(not addressable)"
|
||||
case Memory : return self.Memory.String()
|
||||
case Offset : return self.Offset.String()
|
||||
case Reference : return self.Reference.String()
|
||||
default : return "(invalid addressable)"
|
||||
}
|
||||
}
|
||||
|
||||
// MemoryOperand represents a memory operand for an instruction.
|
||||
type MemoryOperand struct {
|
||||
refs int64
|
||||
Size int
|
||||
Addr Addressable
|
||||
Mask RegisterMask
|
||||
Masked bool
|
||||
Broadcast uint8
|
||||
}
|
||||
|
||||
const (
|
||||
_Sizes = 0b10000000100010111 // bit-mask for valid sizes (0, 1, 2, 4, 8, 16)
|
||||
)
|
||||
|
||||
func (self *MemoryOperand) isVMX(evex bool) bool {
|
||||
return self.Addr.Type == Memory && self.Addr.Memory.isVMX(evex)
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) isVMY(evex bool) bool {
|
||||
return self.Addr.Type == Memory && self.Addr.Memory.isVMY(evex)
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) isVMZ() bool {
|
||||
return self.Addr.Type == Memory && self.Addr.Memory.isVMZ()
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) isMem() bool {
|
||||
if (_Sizes & (1 << self.Broadcast)) == 0 {
|
||||
return false
|
||||
} else if self.Addr.Type == Memory {
|
||||
return self.Addr.Memory.isMem()
|
||||
} else if self.Addr.Type == Offset {
|
||||
return true
|
||||
} else if self.Addr.Type == Reference {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) isSize(n int) bool {
|
||||
return self.Size == 0 || self.Size == n
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) isBroadcast(n int, b uint8) bool {
|
||||
return self.Size == n && self.Broadcast == b
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) formatMask() string {
|
||||
if !self.Masked {
|
||||
return ""
|
||||
} else {
|
||||
return self.Mask.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) formatBroadcast() string {
|
||||
if self.Broadcast == 0 {
|
||||
return ""
|
||||
} else {
|
||||
return fmt.Sprintf("{1to%d}", self.Broadcast)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) ensureAddrValid() {
|
||||
switch self.Addr.Type {
|
||||
case None : break
|
||||
case Memory : self.Addr.Memory.EnsureValid()
|
||||
case Offset : break
|
||||
case Reference : break
|
||||
default : panic("invalid address type")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) ensureSizeValid() {
|
||||
if (_Sizes & (1 << self.Size)) == 0 {
|
||||
panic("invalid memory operand size")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MemoryOperand) ensureBroadcastValid() {
|
||||
if (_Sizes & (1 << self.Broadcast)) == 0 {
|
||||
panic("invalid memory operand broadcast")
|
||||
}
|
||||
}
|
||||
|
||||
// Free decreases the reference count of a MemoryOperand, if the
|
||||
// refcount drops to 0, the Label will be recycled.
|
||||
func (self *MemoryOperand) Free() {
|
||||
if atomic.AddInt64(&self.refs, -1) == 0 {
|
||||
freeMemoryOperand(self)
|
||||
}
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self *MemoryOperand) String() string {
|
||||
return self.Addr.String() + self.formatMask() + self.formatBroadcast()
|
||||
}
|
||||
|
||||
// Retain increases the reference count of a MemoryOperand.
|
||||
func (self *MemoryOperand) Retain() *MemoryOperand {
|
||||
atomic.AddInt64(&self.refs, 1)
|
||||
return self
|
||||
}
|
||||
|
||||
// EnsureValid checks if the memory operand is valid, if not, it panics.
|
||||
func (self *MemoryOperand) EnsureValid() {
|
||||
self.ensureAddrValid()
|
||||
self.ensureSizeValid()
|
||||
self.ensureBroadcastValid()
|
||||
}
|
||||
|
||||
// MemoryAddress represents a memory address.
|
||||
type MemoryAddress struct {
|
||||
Base Register
|
||||
Index Register
|
||||
Scale uint8
|
||||
Displacement int32
|
||||
}
|
||||
|
||||
const (
|
||||
_Scales = 0b100010111 // bit-mask for valid scales (0, 1, 2, 4, 8)
|
||||
)
|
||||
|
||||
func (self *MemoryAddress) isVMX(evex bool) bool {
|
||||
return self.isMemBase() && (self.Index == nil || isXMM(self.Index) || (evex && isEVEXXMM(self.Index)))
|
||||
}
|
||||
|
||||
func (self *MemoryAddress) isVMY(evex bool) bool {
|
||||
return self.isMemBase() && (self.Index == nil || isYMM(self.Index) || (evex && isEVEXYMM(self.Index)))
|
||||
}
|
||||
|
||||
func (self *MemoryAddress) isVMZ() bool {
|
||||
return self.isMemBase() && (self.Index == nil || isZMM(self.Index))
|
||||
}
|
||||
|
||||
func (self *MemoryAddress) isMem() bool {
|
||||
return self.isMemBase() && (self.Index == nil || isReg64(self.Index))
|
||||
}
|
||||
|
||||
func (self *MemoryAddress) isMemBase() bool {
|
||||
return (self.Base == nil || isReg64(self.Base)) && // `Base` must be 64-bit if present
|
||||
(self.Scale == 0) == (self.Index == nil) && // `Scale` and `Index` depends on each other
|
||||
(_Scales & (1 << self.Scale)) != 0 // `Scale` can only be 0, 1, 2, 4 or 8
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self *MemoryAddress) String() string {
|
||||
var dp int
|
||||
var sb strings.Builder
|
||||
|
||||
/* the displacement part */
|
||||
if dp = int(self.Displacement); dp != 0 {
|
||||
sb.WriteString(strconv.Itoa(dp))
|
||||
}
|
||||
|
||||
/* the base register */
|
||||
if sb.WriteByte('('); self.Base != nil {
|
||||
sb.WriteByte('%')
|
||||
sb.WriteString(self.Base.String())
|
||||
}
|
||||
|
||||
/* index is optional */
|
||||
if self.Index != nil {
|
||||
sb.WriteString(",%")
|
||||
sb.WriteString(self.Index.String())
|
||||
|
||||
/* scale is also optional */
|
||||
if self.Scale >= 2 {
|
||||
sb.WriteByte(',')
|
||||
sb.WriteString(strconv.Itoa(int(self.Scale)))
|
||||
}
|
||||
}
|
||||
|
||||
/* close the bracket */
|
||||
sb.WriteByte(')')
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// EnsureValid checks if the memory address is valid, if not, it panics.
|
||||
func (self *MemoryAddress) EnsureValid() {
|
||||
if !self.isMemBase() || (self.Index != nil && !isIndexable(self.Index)) {
|
||||
panic("not a valid memory address")
|
||||
}
|
||||
}
|
||||
|
||||
// Ref constructs a memory reference to a label.
|
||||
func Ref(ref *Label) (v *MemoryOperand) {
|
||||
v = CreateMemoryOperand()
|
||||
v.Addr.Type = Reference
|
||||
v.Addr.Reference = ref
|
||||
return
|
||||
}
|
||||
|
||||
// Abs construct a simple memory address that represents absolute addressing.
|
||||
func Abs(disp int32) *MemoryOperand {
|
||||
return Sib(nil, nil, 0, disp)
|
||||
}
|
||||
|
||||
// Ptr constructs a simple memory operand with base and displacement.
|
||||
func Ptr(base Register, disp int32) *MemoryOperand {
|
||||
return Sib(base, nil, 0, disp)
|
||||
}
|
||||
|
||||
// Sib constructs a simple memory operand that represents a complete memory address.
|
||||
func Sib(base Register, index Register, scale uint8, disp int32) (v *MemoryOperand) {
|
||||
v = CreateMemoryOperand()
|
||||
v.Addr.Type = Memory
|
||||
v.Addr.Memory.Base = base
|
||||
v.Addr.Memory.Index = index
|
||||
v.Addr.Memory.Scale = scale
|
||||
v.Addr.Memory.Displacement = disp
|
||||
v.EnsureValid()
|
||||
return
|
||||
}
|
||||
|
||||
/** Operand Matching Helpers **/
|
||||
|
||||
const _IntMask =
|
||||
(1 << reflect.Int ) |
|
||||
(1 << reflect.Int8 ) |
|
||||
(1 << reflect.Int16 ) |
|
||||
(1 << reflect.Int32 ) |
|
||||
(1 << reflect.Int64 ) |
|
||||
(1 << reflect.Uint ) |
|
||||
(1 << reflect.Uint8 ) |
|
||||
(1 << reflect.Uint16 ) |
|
||||
(1 << reflect.Uint32 ) |
|
||||
(1 << reflect.Uint64 ) |
|
||||
(1 << reflect.Uintptr)
|
||||
|
||||
func isInt(k reflect.Kind) bool {
|
||||
return (_IntMask & (1 << k)) != 0
|
||||
}
|
||||
|
||||
func asInt64(v interface{}) (int64, bool) {
|
||||
if isSpecial(v) {
|
||||
return 0, false
|
||||
} else if x := efaceOf(v); isInt(x.kind()) {
|
||||
return x.toInt64(), true
|
||||
} else {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func inRange(v interface{}, low int64, high int64) bool {
|
||||
x, ok := asInt64(v)
|
||||
return ok && x >= low && x <= high
|
||||
}
|
||||
|
||||
func isSpecial(v interface{}) bool {
|
||||
switch v.(type) {
|
||||
case Register8 : return true
|
||||
case Register16 : return true
|
||||
case Register32 : return true
|
||||
case Register64 : return true
|
||||
case KRegister : return true
|
||||
case MMRegister : return true
|
||||
case XMMRegister : return true
|
||||
case YMMRegister : return true
|
||||
case ZMMRegister : return true
|
||||
case RelativeOffset : return true
|
||||
case RoundingControl : return true
|
||||
case ExceptionControl : return true
|
||||
default : return false
|
||||
}
|
||||
}
|
||||
|
||||
func isIndexable(v interface{}) bool {
|
||||
return isZMM(v) || isReg64(v) || isEVEXXMM(v) || isEVEXYMM(v)
|
||||
}
|
||||
|
||||
func isImm4 (v interface{}) bool { return inRange(v, 0, 15) }
|
||||
func isImm8 (v interface{}) bool { return inRange(v, math.MinInt8, math.MaxUint8) }
|
||||
func isImm16 (v interface{}) bool { return inRange(v, math.MinInt16, math.MaxUint16) }
|
||||
func isImm32 (v interface{}) bool { return inRange(v, math.MinInt32, math.MaxUint32) }
|
||||
func isImm64 (v interface{}) bool { _, r := asInt64(v) ; return r }
|
||||
func isConst1 (v interface{}) bool { x, r := asInt64(v) ; return r && x == 1 }
|
||||
func isConst3 (v interface{}) bool { x, r := asInt64(v) ; return r && x == 3 }
|
||||
func isRel8 (v interface{}) bool { x, r := v.(RelativeOffset) ; return r && x >= math.MinInt8 && x <= math.MaxInt8 }
|
||||
func isRel32 (v interface{}) bool { _, r := v.(RelativeOffset) ; return r }
|
||||
func isLabel (v interface{}) bool { _, r := v.(*Label) ; return r }
|
||||
func isReg8 (v interface{}) bool { _, r := v.(Register8) ; return r }
|
||||
func isReg8REX (v interface{}) bool { x, r := v.(Register8) ; return r && (x & 0x80) == 0 && x >= SPL }
|
||||
func isReg16 (v interface{}) bool { _, r := v.(Register16) ; return r }
|
||||
func isReg32 (v interface{}) bool { _, r := v.(Register32) ; return r }
|
||||
func isReg64 (v interface{}) bool { _, r := v.(Register64) ; return r }
|
||||
func isMM (v interface{}) bool { _, r := v.(MMRegister) ; return r }
|
||||
func isXMM (v interface{}) bool { x, r := v.(XMMRegister) ; return r && x <= XMM15 }
|
||||
func isEVEXXMM (v interface{}) bool { _, r := v.(XMMRegister) ; return r }
|
||||
func isXMMk (v interface{}) bool { x, r := v.(MaskedRegister) ; return isXMM(v) || (r && isXMM(x.Reg) && !x.Mask.Z) }
|
||||
func isXMMkz (v interface{}) bool { x, r := v.(MaskedRegister) ; return isXMM(v) || (r && isXMM(x.Reg)) }
|
||||
func isYMM (v interface{}) bool { x, r := v.(YMMRegister) ; return r && x <= YMM15 }
|
||||
func isEVEXYMM (v interface{}) bool { _, r := v.(YMMRegister) ; return r }
|
||||
func isYMMk (v interface{}) bool { x, r := v.(MaskedRegister) ; return isYMM(v) || (r && isYMM(x.Reg) && !x.Mask.Z) }
|
||||
func isYMMkz (v interface{}) bool { x, r := v.(MaskedRegister) ; return isYMM(v) || (r && isYMM(x.Reg)) }
|
||||
func isZMM (v interface{}) bool { _, r := v.(ZMMRegister) ; return r }
|
||||
func isZMMk (v interface{}) bool { x, r := v.(MaskedRegister) ; return isZMM(v) || (r && isZMM(x.Reg) && !x.Mask.Z) }
|
||||
func isZMMkz (v interface{}) bool { x, r := v.(MaskedRegister) ; return isZMM(v) || (r && isZMM(x.Reg)) }
|
||||
func isK (v interface{}) bool { _, r := v.(KRegister) ; return r }
|
||||
func isKk (v interface{}) bool { x, r := v.(MaskedRegister) ; return isK(v) || (r && isK(x.Reg) && !x.Mask.Z) }
|
||||
func isM (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isMem() && x.Broadcast == 0 && !x.Masked }
|
||||
func isMk (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isMem() && x.Broadcast == 0 && !(x.Masked && x.Mask.Z) }
|
||||
func isMkz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isMem() && x.Broadcast == 0 }
|
||||
func isM8 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(1) }
|
||||
func isM16 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(2) }
|
||||
func isM16kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(2) }
|
||||
func isM32 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(4) }
|
||||
func isM32k (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMk(v) && x.isSize(4) }
|
||||
func isM32kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(4) }
|
||||
func isM64 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(8) }
|
||||
func isM64k (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMk(v) && x.isSize(8) }
|
||||
func isM64kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(8) }
|
||||
func isM128 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(16) }
|
||||
func isM128kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(16) }
|
||||
func isM256 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(32) }
|
||||
func isM256kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(32) }
|
||||
func isM512 (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isM(v) && x.isSize(64) }
|
||||
func isM512kz (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && isMkz(v) && x.isSize(64) }
|
||||
func isM64M32bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM64(v) || (r && x.isBroadcast(4, 2)) }
|
||||
func isM128M32bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM128(v) || (r && x.isBroadcast(4, 4)) }
|
||||
func isM256M32bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM256(v) || (r && x.isBroadcast(4, 8)) }
|
||||
func isM512M32bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM512(v) || (r && x.isBroadcast(4, 16)) }
|
||||
func isM128M64bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM128(v) || (r && x.isBroadcast(8, 2)) }
|
||||
func isM256M64bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM256(v) || (r && x.isBroadcast(8, 4)) }
|
||||
func isM512M64bcst (v interface{}) bool { x, r := v.(*MemoryOperand) ; return isM512(v) || (r && x.isBroadcast(8, 8)) }
|
||||
func isVMX (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMX(false) && !x.Masked }
|
||||
func isEVEXVMX (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMX(true) && !x.Masked }
|
||||
func isVMXk (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMX(true) }
|
||||
func isVMY (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMY(false) && !x.Masked }
|
||||
func isEVEXVMY (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMY(true) && !x.Masked }
|
||||
func isVMYk (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMY(true) }
|
||||
func isVMZ (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMZ() && !x.Masked }
|
||||
func isVMZk (v interface{}) bool { x, r := v.(*MemoryOperand) ; return r && x.isVMZ() }
|
||||
func isSAE (v interface{}) bool { _, r := v.(ExceptionControl) ; return r }
|
||||
func isER (v interface{}) bool { _, r := v.(RoundingControl) ; return r }
|
||||
|
||||
func isImmExt(v interface{}, ext int, min int64, max int64) bool {
|
||||
if x, ok := asInt64(v); !ok {
|
||||
return false
|
||||
} else if m := int64(1) << (8 * ext); x < m && x >= m + min {
|
||||
return true
|
||||
} else {
|
||||
return x <= max && x >= min
|
||||
}
|
||||
}
|
||||
|
||||
func isImm8Ext(v interface{}, ext int) bool {
|
||||
return isImmExt(v, ext, math.MinInt8, math.MaxInt8)
|
||||
}
|
||||
|
||||
func isImm32Ext(v interface{}, ext int) bool {
|
||||
return isImmExt(v, ext, math.MinInt32, math.MaxInt32)
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`sync`
|
||||
)
|
||||
|
||||
var (
|
||||
labelPool sync.Pool
|
||||
programPool sync.Pool
|
||||
instructionPool sync.Pool
|
||||
memoryOperandPool sync.Pool
|
||||
)
|
||||
|
||||
func freeLabel(v *Label) {
|
||||
labelPool.Put(v)
|
||||
}
|
||||
|
||||
func clearLabel(p *Label) *Label {
|
||||
*p = Label{}
|
||||
return p
|
||||
}
|
||||
|
||||
// CreateLabel creates a new Label, it may allocate a new one or grab one from a pool.
|
||||
func CreateLabel(name string) *Label {
|
||||
var p *Label
|
||||
var v interface{}
|
||||
|
||||
/* attempt to grab from the pool */
|
||||
if v = labelPool.Get(); v == nil {
|
||||
p = new(Label)
|
||||
} else {
|
||||
p = clearLabel(v.(*Label))
|
||||
}
|
||||
|
||||
/* initialize the label */
|
||||
p.refs = 1
|
||||
p.Name = name
|
||||
return p
|
||||
}
|
||||
|
||||
func newProgram(arch *Arch) *Program {
|
||||
var p *Program
|
||||
var v interface{}
|
||||
|
||||
/* attempt to grab from the pool */
|
||||
if v = programPool.Get(); v == nil {
|
||||
p = new(Program)
|
||||
} else {
|
||||
p = clearProgram(v.(*Program))
|
||||
}
|
||||
|
||||
/* initialize the program */
|
||||
p.arch = arch
|
||||
return p
|
||||
}
|
||||
|
||||
func freeProgram(p *Program) {
|
||||
programPool.Put(p)
|
||||
}
|
||||
|
||||
func clearProgram(p *Program) *Program {
|
||||
*p = Program{}
|
||||
return p
|
||||
}
|
||||
|
||||
func newInstruction(name string, argc int, argv Operands) *Instruction {
|
||||
var v interface{}
|
||||
var p *Instruction
|
||||
|
||||
/* attempt to grab from the pool */
|
||||
if v = instructionPool.Get(); v == nil {
|
||||
p = new(Instruction)
|
||||
} else {
|
||||
p = clearInstruction(v.(*Instruction))
|
||||
}
|
||||
|
||||
/* initialize the instruction */
|
||||
p.name = name
|
||||
p.argc = argc
|
||||
p.argv = argv
|
||||
return p
|
||||
}
|
||||
|
||||
func freeInstruction(v *Instruction) {
|
||||
instructionPool.Put(v)
|
||||
}
|
||||
|
||||
func clearInstruction(p *Instruction) *Instruction {
|
||||
*p = Instruction { prefix: p.prefix[:0] }
|
||||
return p
|
||||
}
|
||||
|
||||
func freeMemoryOperand(m *MemoryOperand) {
|
||||
memoryOperandPool.Put(m)
|
||||
}
|
||||
|
||||
func clearMemoryOperand(m *MemoryOperand) *MemoryOperand {
|
||||
*m = MemoryOperand{}
|
||||
return m
|
||||
}
|
||||
|
||||
// CreateMemoryOperand creates a new MemoryOperand, it may allocate a new one or grab one from a pool.
|
||||
func CreateMemoryOperand() *MemoryOperand {
|
||||
var v interface{}
|
||||
var p *MemoryOperand
|
||||
|
||||
/* attempt to grab from the pool */
|
||||
if v = memoryOperandPool.Get(); v == nil {
|
||||
p = new(MemoryOperand)
|
||||
} else {
|
||||
p = clearMemoryOperand(v.(*MemoryOperand))
|
||||
}
|
||||
|
||||
/* initialize the memory operand */
|
||||
p.refs = 1
|
||||
return p
|
||||
}
|
@ -0,0 +1,542 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`math`
|
||||
`math/bits`
|
||||
|
||||
`github.com/chenzhuoyu/iasm/expr`
|
||||
)
|
||||
|
||||
type (
|
||||
_PseudoType int
|
||||
_InstructionEncoder func(*Program, ...interface{}) *Instruction
|
||||
)
|
||||
|
||||
const (
|
||||
_PseudoNop _PseudoType = iota + 1
|
||||
_PseudoByte
|
||||
_PseudoWord
|
||||
_PseudoLong
|
||||
_PseudoQuad
|
||||
_PseudoData
|
||||
_PseudoAlign
|
||||
)
|
||||
|
||||
func (self _PseudoType) String() string {
|
||||
switch self {
|
||||
case _PseudoNop : return ".nop"
|
||||
case _PseudoByte : return ".byte"
|
||||
case _PseudoWord : return ".word"
|
||||
case _PseudoLong : return ".long"
|
||||
case _PseudoQuad : return ".quad"
|
||||
case _PseudoData : return ".data"
|
||||
case _PseudoAlign : return ".align"
|
||||
default : panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
type _Pseudo struct {
|
||||
kind _PseudoType
|
||||
data []byte
|
||||
uint uint64
|
||||
expr *expr.Expr
|
||||
}
|
||||
|
||||
func (self *_Pseudo) free() {
|
||||
if self.expr != nil {
|
||||
self.expr.Free()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encode(m *[]byte, pc uintptr) int {
|
||||
switch self.kind {
|
||||
case _PseudoNop : return 0
|
||||
case _PseudoByte : self.encodeByte(m) ; return 1
|
||||
case _PseudoWord : self.encodeWord(m) ; return 2
|
||||
case _PseudoLong : self.encodeLong(m) ; return 4
|
||||
case _PseudoQuad : self.encodeQuad(m) ; return 8
|
||||
case _PseudoData : self.encodeData(m) ; return len(self.data)
|
||||
case _PseudoAlign : self.encodeAlign(m, pc) ; return self.alignSize(pc)
|
||||
default : panic("invalid pseudo instruction")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) evalExpr(low int64, high int64) int64 {
|
||||
if v, err := self.expr.Evaluate(); err != nil {
|
||||
panic(err)
|
||||
} else if v < low || v > high {
|
||||
panic(fmt.Sprintf("expression out of range [%d, %d]: %d", low, high, v))
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) alignSize(pc uintptr) int {
|
||||
if !ispow2(self.uint) {
|
||||
panic(fmt.Sprintf("aligment should be a power of 2, not %d", self.uint))
|
||||
} else {
|
||||
return align(int(pc), bits.TrailingZeros64(self.uint)) - int(pc)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeData(m *[]byte) {
|
||||
if m != nil {
|
||||
*m = append(*m, self.data...)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeByte(m *[]byte) {
|
||||
if m != nil {
|
||||
append8(m, byte(self.evalExpr(math.MinInt8, math.MaxUint8)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeWord(m *[]byte) {
|
||||
if m != nil {
|
||||
append16(m, uint16(self.evalExpr(math.MinInt16, math.MaxUint16)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeLong(m *[]byte) {
|
||||
if m != nil {
|
||||
append32(m, uint32(self.evalExpr(math.MinInt32, math.MaxUint32)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeQuad(m *[]byte) {
|
||||
if m != nil {
|
||||
if v, err := self.expr.Evaluate(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
append64(m, uint64(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Pseudo) encodeAlign(m *[]byte, pc uintptr) {
|
||||
if m != nil {
|
||||
if self.expr == nil {
|
||||
expandmm(m, self.alignSize(pc), 0)
|
||||
} else {
|
||||
expandmm(m, self.alignSize(pc), byte(self.evalExpr(math.MinInt8, math.MaxUint8)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Operands represents a sequence of operand required by an instruction.
|
||||
type Operands [_N_args]interface{}
|
||||
|
||||
// InstructionDomain represents the domain of an instruction.
|
||||
type InstructionDomain uint8
|
||||
|
||||
const (
|
||||
DomainGeneric InstructionDomain = iota
|
||||
DomainMMXSSE
|
||||
DomainAVX
|
||||
DomainFMA
|
||||
DomainCrypto
|
||||
DomainMask
|
||||
DomainAMDSpecific
|
||||
DomainMisc
|
||||
DomainPseudo
|
||||
)
|
||||
|
||||
type (
|
||||
_BranchType uint8
|
||||
)
|
||||
|
||||
const (
|
||||
_B_none _BranchType = iota
|
||||
_B_conditional
|
||||
_B_unconditional
|
||||
)
|
||||
|
||||
// Instruction represents an unencoded instruction.
|
||||
type Instruction struct {
|
||||
next *Instruction
|
||||
pc uintptr
|
||||
nb int
|
||||
len int
|
||||
argc int
|
||||
name string
|
||||
argv Operands
|
||||
forms [_N_forms]_Encoding
|
||||
pseudo _Pseudo
|
||||
branch _BranchType
|
||||
domain InstructionDomain
|
||||
prefix []byte
|
||||
}
|
||||
|
||||
func (self *Instruction) add(flags int, encoder func(m *_Encoding, v []interface{})) {
|
||||
self.forms[self.len].flags = flags
|
||||
self.forms[self.len].encoder = encoder
|
||||
self.len++
|
||||
}
|
||||
|
||||
func (self *Instruction) free() {
|
||||
self.clear()
|
||||
self.pseudo.free()
|
||||
freeInstruction(self)
|
||||
}
|
||||
|
||||
func (self *Instruction) clear() {
|
||||
for i := 0; i < self.argc; i++ {
|
||||
if v, ok := self.argv[i].(Disposable); ok {
|
||||
v.Free()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Instruction) check(e *_Encoding) bool {
|
||||
if (e.flags & _F_rel1) != 0 {
|
||||
return isRel8(self.argv[0])
|
||||
} else if (e.flags & _F_rel4) != 0 {
|
||||
return isRel32(self.argv[0]) || isLabel(self.argv[0])
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Instruction) encode(m *[]byte) int {
|
||||
n := math.MaxInt64
|
||||
p := (*_Encoding)(nil)
|
||||
|
||||
/* encode prefixes if any */
|
||||
if self.nb = len(self.prefix); m != nil {
|
||||
*m = append(*m, self.prefix...)
|
||||
}
|
||||
|
||||
/* check for pseudo-instructions */
|
||||
if self.pseudo.kind != 0 {
|
||||
self.nb += self.pseudo.encode(m, self.pc)
|
||||
return self.nb
|
||||
}
|
||||
|
||||
/* find the shortest encoding */
|
||||
for i := 0; i < self.len; i++ {
|
||||
if e := &self.forms[i]; self.check(e) {
|
||||
if v := e.encode(self.argv[:self.argc]); v < n {
|
||||
n = v
|
||||
p = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add to buffer if needed */
|
||||
if m != nil {
|
||||
*m = append(*m, p.bytes[:n]...)
|
||||
}
|
||||
|
||||
/* update the instruction length */
|
||||
self.nb += n
|
||||
return self.nb
|
||||
}
|
||||
|
||||
/** Instruction Prefixes **/
|
||||
|
||||
const (
|
||||
_P_cs = 0x2e
|
||||
_P_ds = 0x3e
|
||||
_P_es = 0x26
|
||||
_P_fs = 0x64
|
||||
_P_gs = 0x65
|
||||
_P_ss = 0x36
|
||||
_P_lock = 0xf0
|
||||
)
|
||||
|
||||
// CS overrides the memory operation of this instruction to CS.
|
||||
func (self *Instruction) CS() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_cs)
|
||||
return self
|
||||
}
|
||||
|
||||
// DS overrides the memory operation of this instruction to DS,
|
||||
// this is the default section for most instructions if not specified.
|
||||
func (self *Instruction) DS() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_ds)
|
||||
return self
|
||||
}
|
||||
|
||||
// ES overrides the memory operation of this instruction to ES.
|
||||
func (self *Instruction) ES() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_es)
|
||||
return self
|
||||
}
|
||||
|
||||
// FS overrides the memory operation of this instruction to FS.
|
||||
func (self *Instruction) FS() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_fs)
|
||||
return self
|
||||
}
|
||||
|
||||
// GS overrides the memory operation of this instruction to GS.
|
||||
func (self *Instruction) GS() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_gs)
|
||||
return self
|
||||
}
|
||||
|
||||
// SS overrides the memory operation of this instruction to SS.
|
||||
func (self *Instruction) SS() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_ss)
|
||||
return self
|
||||
}
|
||||
|
||||
// LOCK causes the processor's LOCK# signal to be asserted during execution of
|
||||
// the accompanying instruction (turns the instruction into an atomic instruction).
|
||||
// In a multiprocessor environment, the LOCK# signal insures that the processor
|
||||
// has exclusive use of any shared memory while the signal is asserted.
|
||||
func (self *Instruction) LOCK() *Instruction {
|
||||
self.prefix = append(self.prefix, _P_lock)
|
||||
return self
|
||||
}
|
||||
|
||||
/** Basic Instruction Properties **/
|
||||
|
||||
// Name returns the instruction name.
|
||||
func (self *Instruction) Name() string {
|
||||
return self.name
|
||||
}
|
||||
|
||||
// Domain returns the domain of this instruction.
|
||||
func (self *Instruction) Domain() InstructionDomain {
|
||||
return self.domain
|
||||
}
|
||||
|
||||
// Operands returns the operands of this instruction.
|
||||
func (self *Instruction) Operands() []interface{} {
|
||||
return self.argv[:self.argc]
|
||||
}
|
||||
|
||||
// Program represents a sequence of instructions.
|
||||
type Program struct {
|
||||
arch *Arch
|
||||
head *Instruction
|
||||
tail *Instruction
|
||||
}
|
||||
|
||||
const (
|
||||
_N_near = 2 // near-branch (-128 ~ +127) takes 2 bytes to encode
|
||||
_N_far_cond = 6 // conditional far-branch takes 6 bytes to encode
|
||||
_N_far_uncond = 5 // unconditional far-branch takes 5 bytes to encode
|
||||
)
|
||||
|
||||
func (self *Program) clear() {
|
||||
for p, q := self.head, self.head; p != nil; p = q {
|
||||
q = p.next
|
||||
p.free()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Program) alloc(name string, argc int, argv Operands) *Instruction {
|
||||
p := self.tail
|
||||
q := newInstruction(name, argc, argv)
|
||||
|
||||
/* attach to tail if any */
|
||||
if p != nil {
|
||||
p.next = q
|
||||
} else {
|
||||
self.head = q
|
||||
}
|
||||
|
||||
/* set the new tail */
|
||||
self.tail = q
|
||||
return q
|
||||
}
|
||||
|
||||
func (self *Program) pseudo(kind _PseudoType) (p *Instruction) {
|
||||
p = self.alloc(kind.String(), 0, Operands{})
|
||||
p.domain = DomainPseudo
|
||||
p.pseudo.kind = kind
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Program) require(isa ISA) {
|
||||
if !self.arch.HasISA(isa) {
|
||||
panic("ISA '" + isa.String() + "' was not enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Program) branchSize(p *Instruction) int {
|
||||
switch p.branch {
|
||||
case _B_none : panic("p is not a branch")
|
||||
case _B_conditional : return _N_far_cond
|
||||
case _B_unconditional : return _N_far_uncond
|
||||
default : panic("invalid instruction")
|
||||
}
|
||||
}
|
||||
|
||||
/** Pseudo-Instructions **/
|
||||
|
||||
// Byte is a pseudo-instruction to add raw byte to the assembled code.
|
||||
func (self *Program) Byte(v *expr.Expr) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoByte)
|
||||
p.pseudo.expr = v
|
||||
return
|
||||
}
|
||||
|
||||
// Word is a pseudo-instruction to add raw uint16 as little-endian to the assembled code.
|
||||
func (self *Program) Word(v *expr.Expr) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoWord)
|
||||
p.pseudo.expr = v
|
||||
return
|
||||
}
|
||||
|
||||
// Long is a pseudo-instruction to add raw uint32 as little-endian to the assembled code.
|
||||
func (self *Program) Long(v *expr.Expr) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoLong)
|
||||
p.pseudo.expr = v
|
||||
return
|
||||
}
|
||||
|
||||
// Quad is a pseudo-instruction to add raw uint64 as little-endian to the assembled code.
|
||||
func (self *Program) Quad(v *expr.Expr) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoQuad)
|
||||
p.pseudo.expr = v
|
||||
return
|
||||
}
|
||||
|
||||
// Data is a pseudo-instruction to add raw bytes to the assembled code.
|
||||
func (self *Program) Data(v []byte) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoData)
|
||||
p.pseudo.data = v
|
||||
return
|
||||
}
|
||||
|
||||
// Align is a pseudo-instruction to ensure the PC is aligned to a certain value.
|
||||
func (self *Program) Align(align uint64, padding *expr.Expr) (p *Instruction) {
|
||||
p = self.pseudo(_PseudoAlign)
|
||||
p.pseudo.uint = align
|
||||
p.pseudo.expr = padding
|
||||
return
|
||||
}
|
||||
|
||||
/** Program Assembler **/
|
||||
|
||||
// Free returns the Program object into pool.
|
||||
// Any operation performed after Free is undefined behavior.
|
||||
//
|
||||
// NOTE: This also frees all the instructions, labels, memory
|
||||
// operands and expressions associated with this program.
|
||||
//
|
||||
func (self *Program) Free() {
|
||||
self.clear()
|
||||
freeProgram(self)
|
||||
}
|
||||
|
||||
// Link pins a label at the current position.
|
||||
func (self *Program) Link(p *Label) {
|
||||
if p.Dest != nil {
|
||||
panic("lable was alreay linked")
|
||||
} else {
|
||||
p.Dest = self.pseudo(_PseudoNop)
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble assembles and links the entire program into machine code.
|
||||
func (self *Program) Assemble(pc uintptr) (ret []byte) {
|
||||
orig := pc
|
||||
next := true
|
||||
offs := uintptr(0)
|
||||
|
||||
/* Pass 0: PC-precompute, assume all labeled branches are far-branches. */
|
||||
for p := self.head; p != nil; p = p.next {
|
||||
if p.pc = pc; !isLabel(p.argv[0]) || p.branch == _B_none {
|
||||
pc += uintptr(p.encode(nil))
|
||||
} else {
|
||||
pc += uintptr(self.branchSize(p))
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate space for the machine code */
|
||||
nb := int(pc - orig)
|
||||
ret = make([]byte, 0, nb)
|
||||
|
||||
/* Pass 1: adjust all the jumps */
|
||||
for next {
|
||||
next = false
|
||||
offs = uintptr(0)
|
||||
|
||||
/* scan all the branches */
|
||||
for p := self.head; p != nil; p = p.next {
|
||||
var ok bool
|
||||
var lb *Label
|
||||
|
||||
/* re-calculate the alignment here */
|
||||
if nb = p.nb; p.pseudo.kind == _PseudoAlign {
|
||||
p.pc -= offs
|
||||
offs += uintptr(nb - p.encode(nil))
|
||||
continue
|
||||
}
|
||||
|
||||
/* adjust the program counter */
|
||||
p.pc -= offs
|
||||
lb, ok = p.argv[0].(*Label)
|
||||
|
||||
/* only care about labeled far-branches */
|
||||
if !ok || p.nb == _N_near || p.branch == _B_none {
|
||||
continue
|
||||
}
|
||||
|
||||
/* calculate the jump offset */
|
||||
size := self.branchSize(p)
|
||||
diff := lb.offset(p.pc, size)
|
||||
|
||||
/* too far to be a near jump */
|
||||
if diff > 127 || diff < -128 {
|
||||
p.nb = size
|
||||
continue
|
||||
}
|
||||
|
||||
/* a far jump becomes a near jump, calculate
|
||||
* the PC adjustment value and assemble again */
|
||||
next = true
|
||||
p.nb = _N_near
|
||||
offs += uintptr(size - _N_near)
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 3: link all the cross-references */
|
||||
for p := self.head; p != nil; p = p.next {
|
||||
for i := 0; i < p.argc; i++ {
|
||||
var ok bool
|
||||
var lb *Label
|
||||
var op *MemoryOperand
|
||||
|
||||
/* resolve labels */
|
||||
if lb, ok = p.argv[i].(*Label); ok {
|
||||
p.argv[i] = lb.offset(p.pc, p.nb)
|
||||
continue
|
||||
}
|
||||
|
||||
/* check for memory operands */
|
||||
if op, ok = p.argv[i].(*MemoryOperand); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
/* check for label references */
|
||||
if op.Addr.Type != Reference {
|
||||
continue
|
||||
}
|
||||
|
||||
/* replace the label with the real offset */
|
||||
op.Addr.Type = Offset
|
||||
op.Addr.Offset = op.Addr.Reference.offset(p.pc, p.nb)
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 4: actually encode all the instructions */
|
||||
for p := self.head; p != nil; p = p.next {
|
||||
p.encode(&ret)
|
||||
}
|
||||
|
||||
/* all done */
|
||||
return ret
|
||||
}
|
||||
|
||||
// AssembleAndFree is like Assemble, but it frees the Program after assembling.
|
||||
func (self *Program) AssembleAndFree(pc uintptr) (ret []byte) {
|
||||
ret = self.Assemble(pc)
|
||||
self.Free()
|
||||
return
|
||||
}
|
@ -0,0 +1,677 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
// Register represents a hardware register.
|
||||
type Register interface {
|
||||
fmt.Stringer
|
||||
implRegister()
|
||||
}
|
||||
|
||||
type (
|
||||
Register8 byte
|
||||
Register16 byte
|
||||
Register32 byte
|
||||
Register64 byte
|
||||
)
|
||||
|
||||
type (
|
||||
KRegister byte
|
||||
MMRegister byte
|
||||
XMMRegister byte
|
||||
YMMRegister byte
|
||||
ZMMRegister byte
|
||||
)
|
||||
|
||||
// RegisterMask is a KRegister used to mask another register.
|
||||
type RegisterMask struct {
|
||||
Z bool
|
||||
K KRegister
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self RegisterMask) String() string {
|
||||
if !self.Z {
|
||||
return fmt.Sprintf("{%%%s}", self.K)
|
||||
} else {
|
||||
return fmt.Sprintf("{%%%s}{z}", self.K)
|
||||
}
|
||||
}
|
||||
|
||||
// MaskedRegister is a Register masked by a RegisterMask.
|
||||
type MaskedRegister struct {
|
||||
Reg Register
|
||||
Mask RegisterMask
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (self MaskedRegister) String() string {
|
||||
return self.Reg.String() + self.Mask.String()
|
||||
}
|
||||
|
||||
const (
|
||||
AL Register8 = iota
|
||||
CL
|
||||
DL
|
||||
BL
|
||||
SPL
|
||||
BPL
|
||||
SIL
|
||||
DIL
|
||||
R8b
|
||||
R9b
|
||||
R10b
|
||||
R11b
|
||||
R12b
|
||||
R13b
|
||||
R14b
|
||||
R15b
|
||||
)
|
||||
|
||||
const (
|
||||
AH = SPL | 0x80
|
||||
CH = BPL | 0x80
|
||||
DH = SIL | 0x80
|
||||
BH = DIL | 0x80
|
||||
)
|
||||
|
||||
const (
|
||||
AX Register16 = iota
|
||||
CX
|
||||
DX
|
||||
BX
|
||||
SP
|
||||
BP
|
||||
SI
|
||||
DI
|
||||
R8w
|
||||
R9w
|
||||
R10w
|
||||
R11w
|
||||
R12w
|
||||
R13w
|
||||
R14w
|
||||
R15w
|
||||
)
|
||||
|
||||
const (
|
||||
EAX Register32 = iota
|
||||
ECX
|
||||
EDX
|
||||
EBX
|
||||
ESP
|
||||
EBP
|
||||
ESI
|
||||
EDI
|
||||
R8d
|
||||
R9d
|
||||
R10d
|
||||
R11d
|
||||
R12d
|
||||
R13d
|
||||
R14d
|
||||
R15d
|
||||
)
|
||||
|
||||
const (
|
||||
RAX Register64 = iota
|
||||
RCX
|
||||
RDX
|
||||
RBX
|
||||
RSP
|
||||
RBP
|
||||
RSI
|
||||
RDI
|
||||
R8
|
||||
R9
|
||||
R10
|
||||
R11
|
||||
R12
|
||||
R13
|
||||
R14
|
||||
R15
|
||||
)
|
||||
|
||||
const (
|
||||
K0 KRegister = iota
|
||||
K1
|
||||
K2
|
||||
K3
|
||||
K4
|
||||
K5
|
||||
K6
|
||||
K7
|
||||
)
|
||||
|
||||
const (
|
||||
MM0 MMRegister = iota
|
||||
MM1
|
||||
MM2
|
||||
MM3
|
||||
MM4
|
||||
MM5
|
||||
MM6
|
||||
MM7
|
||||
)
|
||||
|
||||
const (
|
||||
XMM0 XMMRegister = iota
|
||||
XMM1
|
||||
XMM2
|
||||
XMM3
|
||||
XMM4
|
||||
XMM5
|
||||
XMM6
|
||||
XMM7
|
||||
XMM8
|
||||
XMM9
|
||||
XMM10
|
||||
XMM11
|
||||
XMM12
|
||||
XMM13
|
||||
XMM14
|
||||
XMM15
|
||||
XMM16
|
||||
XMM17
|
||||
XMM18
|
||||
XMM19
|
||||
XMM20
|
||||
XMM21
|
||||
XMM22
|
||||
XMM23
|
||||
XMM24
|
||||
XMM25
|
||||
XMM26
|
||||
XMM27
|
||||
XMM28
|
||||
XMM29
|
||||
XMM30
|
||||
XMM31
|
||||
)
|
||||
|
||||
const (
|
||||
YMM0 YMMRegister = iota
|
||||
YMM1
|
||||
YMM2
|
||||
YMM3
|
||||
YMM4
|
||||
YMM5
|
||||
YMM6
|
||||
YMM7
|
||||
YMM8
|
||||
YMM9
|
||||
YMM10
|
||||
YMM11
|
||||
YMM12
|
||||
YMM13
|
||||
YMM14
|
||||
YMM15
|
||||
YMM16
|
||||
YMM17
|
||||
YMM18
|
||||
YMM19
|
||||
YMM20
|
||||
YMM21
|
||||
YMM22
|
||||
YMM23
|
||||
YMM24
|
||||
YMM25
|
||||
YMM26
|
||||
YMM27
|
||||
YMM28
|
||||
YMM29
|
||||
YMM30
|
||||
YMM31
|
||||
)
|
||||
|
||||
const (
|
||||
ZMM0 ZMMRegister = iota
|
||||
ZMM1
|
||||
ZMM2
|
||||
ZMM3
|
||||
ZMM4
|
||||
ZMM5
|
||||
ZMM6
|
||||
ZMM7
|
||||
ZMM8
|
||||
ZMM9
|
||||
ZMM10
|
||||
ZMM11
|
||||
ZMM12
|
||||
ZMM13
|
||||
ZMM14
|
||||
ZMM15
|
||||
ZMM16
|
||||
ZMM17
|
||||
ZMM18
|
||||
ZMM19
|
||||
ZMM20
|
||||
ZMM21
|
||||
ZMM22
|
||||
ZMM23
|
||||
ZMM24
|
||||
ZMM25
|
||||
ZMM26
|
||||
ZMM27
|
||||
ZMM28
|
||||
ZMM29
|
||||
ZMM30
|
||||
ZMM31
|
||||
)
|
||||
|
||||
func (self Register8) implRegister() {}
|
||||
func (self Register16) implRegister() {}
|
||||
func (self Register32) implRegister() {}
|
||||
func (self Register64) implRegister() {}
|
||||
|
||||
func (self KRegister) implRegister() {}
|
||||
func (self MMRegister) implRegister() {}
|
||||
func (self XMMRegister) implRegister() {}
|
||||
func (self YMMRegister) implRegister() {}
|
||||
func (self ZMMRegister) implRegister() {}
|
||||
|
||||
func (self Register8) String() string { if int(self) >= len(r8names) { return "???" } else { return r8names[self] } }
|
||||
func (self Register16) String() string { if int(self) >= len(r16names) { return "???" } else { return r16names[self] } }
|
||||
func (self Register32) String() string { if int(self) >= len(r32names) { return "???" } else { return r32names[self] } }
|
||||
func (self Register64) String() string { if int(self) >= len(r64names) { return "???" } else { return r64names[self] } }
|
||||
|
||||
func (self KRegister) String() string { if int(self) >= len(knames) { return "???" } else { return knames[self] } }
|
||||
func (self MMRegister) String() string { if int(self) >= len(mmnames) { return "???" } else { return mmnames[self] } }
|
||||
func (self XMMRegister) String() string { if int(self) >= len(xmmnames) { return "???" } else { return xmmnames[self] } }
|
||||
func (self YMMRegister) String() string { if int(self) >= len(ymmnames) { return "???" } else { return ymmnames[self] } }
|
||||
func (self ZMMRegister) String() string { if int(self) >= len(zmmnames) { return "???" } else { return zmmnames[self] } }
|
||||
|
||||
// Registers maps register name into Register instances.
|
||||
var Registers = map[string]Register {
|
||||
"al" : AL,
|
||||
"cl" : CL,
|
||||
"dl" : DL,
|
||||
"bl" : BL,
|
||||
"spl" : SPL,
|
||||
"bpl" : BPL,
|
||||
"sil" : SIL,
|
||||
"dil" : DIL,
|
||||
"r8b" : R8b,
|
||||
"r9b" : R9b,
|
||||
"r10b" : R10b,
|
||||
"r11b" : R11b,
|
||||
"r12b" : R12b,
|
||||
"r13b" : R13b,
|
||||
"r14b" : R14b,
|
||||
"r15b" : R15b,
|
||||
"ah" : AH,
|
||||
"ch" : CH,
|
||||
"dh" : DH,
|
||||
"bh" : BH,
|
||||
"ax" : AX,
|
||||
"cx" : CX,
|
||||
"dx" : DX,
|
||||
"bx" : BX,
|
||||
"sp" : SP,
|
||||
"bp" : BP,
|
||||
"si" : SI,
|
||||
"di" : DI,
|
||||
"r8w" : R8w,
|
||||
"r9w" : R9w,
|
||||
"r10w" : R10w,
|
||||
"r11w" : R11w,
|
||||
"r12w" : R12w,
|
||||
"r13w" : R13w,
|
||||
"r14w" : R14w,
|
||||
"r15w" : R15w,
|
||||
"eax" : EAX,
|
||||
"ecx" : ECX,
|
||||
"edx" : EDX,
|
||||
"ebx" : EBX,
|
||||
"esp" : ESP,
|
||||
"ebp" : EBP,
|
||||
"esi" : ESI,
|
||||
"edi" : EDI,
|
||||
"r8d" : R8d,
|
||||
"r9d" : R9d,
|
||||
"r10d" : R10d,
|
||||
"r11d" : R11d,
|
||||
"r12d" : R12d,
|
||||
"r13d" : R13d,
|
||||
"r14d" : R14d,
|
||||
"r15d" : R15d,
|
||||
"rax" : RAX,
|
||||
"rcx" : RCX,
|
||||
"rdx" : RDX,
|
||||
"rbx" : RBX,
|
||||
"rsp" : RSP,
|
||||
"rbp" : RBP,
|
||||
"rsi" : RSI,
|
||||
"rdi" : RDI,
|
||||
"r8" : R8,
|
||||
"r9" : R9,
|
||||
"r10" : R10,
|
||||
"r11" : R11,
|
||||
"r12" : R12,
|
||||
"r13" : R13,
|
||||
"r14" : R14,
|
||||
"r15" : R15,
|
||||
"k0" : K0,
|
||||
"k1" : K1,
|
||||
"k2" : K2,
|
||||
"k3" : K3,
|
||||
"k4" : K4,
|
||||
"k5" : K5,
|
||||
"k6" : K6,
|
||||
"k7" : K7,
|
||||
"mm0" : MM0,
|
||||
"mm1" : MM1,
|
||||
"mm2" : MM2,
|
||||
"mm3" : MM3,
|
||||
"mm4" : MM4,
|
||||
"mm5" : MM5,
|
||||
"mm6" : MM6,
|
||||
"mm7" : MM7,
|
||||
"xmm0" : XMM0,
|
||||
"xmm1" : XMM1,
|
||||
"xmm2" : XMM2,
|
||||
"xmm3" : XMM3,
|
||||
"xmm4" : XMM4,
|
||||
"xmm5" : XMM5,
|
||||
"xmm6" : XMM6,
|
||||
"xmm7" : XMM7,
|
||||
"xmm8" : XMM8,
|
||||
"xmm9" : XMM9,
|
||||
"xmm10" : XMM10,
|
||||
"xmm11" : XMM11,
|
||||
"xmm12" : XMM12,
|
||||
"xmm13" : XMM13,
|
||||
"xmm14" : XMM14,
|
||||
"xmm15" : XMM15,
|
||||
"xmm16" : XMM16,
|
||||
"xmm17" : XMM17,
|
||||
"xmm18" : XMM18,
|
||||
"xmm19" : XMM19,
|
||||
"xmm20" : XMM20,
|
||||
"xmm21" : XMM21,
|
||||
"xmm22" : XMM22,
|
||||
"xmm23" : XMM23,
|
||||
"xmm24" : XMM24,
|
||||
"xmm25" : XMM25,
|
||||
"xmm26" : XMM26,
|
||||
"xmm27" : XMM27,
|
||||
"xmm28" : XMM28,
|
||||
"xmm29" : XMM29,
|
||||
"xmm30" : XMM30,
|
||||
"xmm31" : XMM31,
|
||||
"ymm0" : YMM0,
|
||||
"ymm1" : YMM1,
|
||||
"ymm2" : YMM2,
|
||||
"ymm3" : YMM3,
|
||||
"ymm4" : YMM4,
|
||||
"ymm5" : YMM5,
|
||||
"ymm6" : YMM6,
|
||||
"ymm7" : YMM7,
|
||||
"ymm8" : YMM8,
|
||||
"ymm9" : YMM9,
|
||||
"ymm10" : YMM10,
|
||||
"ymm11" : YMM11,
|
||||
"ymm12" : YMM12,
|
||||
"ymm13" : YMM13,
|
||||
"ymm14" : YMM14,
|
||||
"ymm15" : YMM15,
|
||||
"ymm16" : YMM16,
|
||||
"ymm17" : YMM17,
|
||||
"ymm18" : YMM18,
|
||||
"ymm19" : YMM19,
|
||||
"ymm20" : YMM20,
|
||||
"ymm21" : YMM21,
|
||||
"ymm22" : YMM22,
|
||||
"ymm23" : YMM23,
|
||||
"ymm24" : YMM24,
|
||||
"ymm25" : YMM25,
|
||||
"ymm26" : YMM26,
|
||||
"ymm27" : YMM27,
|
||||
"ymm28" : YMM28,
|
||||
"ymm29" : YMM29,
|
||||
"ymm30" : YMM30,
|
||||
"ymm31" : YMM31,
|
||||
"zmm0" : ZMM0,
|
||||
"zmm1" : ZMM1,
|
||||
"zmm2" : ZMM2,
|
||||
"zmm3" : ZMM3,
|
||||
"zmm4" : ZMM4,
|
||||
"zmm5" : ZMM5,
|
||||
"zmm6" : ZMM6,
|
||||
"zmm7" : ZMM7,
|
||||
"zmm8" : ZMM8,
|
||||
"zmm9" : ZMM9,
|
||||
"zmm10" : ZMM10,
|
||||
"zmm11" : ZMM11,
|
||||
"zmm12" : ZMM12,
|
||||
"zmm13" : ZMM13,
|
||||
"zmm14" : ZMM14,
|
||||
"zmm15" : ZMM15,
|
||||
"zmm16" : ZMM16,
|
||||
"zmm17" : ZMM17,
|
||||
"zmm18" : ZMM18,
|
||||
"zmm19" : ZMM19,
|
||||
"zmm20" : ZMM20,
|
||||
"zmm21" : ZMM21,
|
||||
"zmm22" : ZMM22,
|
||||
"zmm23" : ZMM23,
|
||||
"zmm24" : ZMM24,
|
||||
"zmm25" : ZMM25,
|
||||
"zmm26" : ZMM26,
|
||||
"zmm27" : ZMM27,
|
||||
"zmm28" : ZMM28,
|
||||
"zmm29" : ZMM29,
|
||||
"zmm30" : ZMM30,
|
||||
"zmm31" : ZMM31,
|
||||
}
|
||||
|
||||
/** Register Name Tables **/
|
||||
|
||||
var r8names = [...]string {
|
||||
AL : "al",
|
||||
CL : "cl",
|
||||
DL : "dl",
|
||||
BL : "bl",
|
||||
SPL : "spl",
|
||||
BPL : "bpl",
|
||||
SIL : "sil",
|
||||
DIL : "dil",
|
||||
R8b : "r8b",
|
||||
R9b : "r9b",
|
||||
R10b : "r10b",
|
||||
R11b : "r11b",
|
||||
R12b : "r12b",
|
||||
R13b : "r13b",
|
||||
R14b : "r14b",
|
||||
R15b : "r15b",
|
||||
AH : "ah",
|
||||
CH : "ch",
|
||||
DH : "dh",
|
||||
BH : "bh",
|
||||
}
|
||||
|
||||
var r16names = [...]string {
|
||||
AX : "ax",
|
||||
CX : "cx",
|
||||
DX : "dx",
|
||||
BX : "bx",
|
||||
SP : "sp",
|
||||
BP : "bp",
|
||||
SI : "si",
|
||||
DI : "di",
|
||||
R8w : "r8w",
|
||||
R9w : "r9w",
|
||||
R10w : "r10w",
|
||||
R11w : "r11w",
|
||||
R12w : "r12w",
|
||||
R13w : "r13w",
|
||||
R14w : "r14w",
|
||||
R15w : "r15w",
|
||||
}
|
||||
|
||||
var r32names = [...]string {
|
||||
EAX : "eax",
|
||||
ECX : "ecx",
|
||||
EDX : "edx",
|
||||
EBX : "ebx",
|
||||
ESP : "esp",
|
||||
EBP : "ebp",
|
||||
ESI : "esi",
|
||||
EDI : "edi",
|
||||
R8d : "r8d",
|
||||
R9d : "r9d",
|
||||
R10d : "r10d",
|
||||
R11d : "r11d",
|
||||
R12d : "r12d",
|
||||
R13d : "r13d",
|
||||
R14d : "r14d",
|
||||
R15d : "r15d",
|
||||
}
|
||||
|
||||
var r64names = [...]string {
|
||||
RAX : "rax",
|
||||
RCX : "rcx",
|
||||
RDX : "rdx",
|
||||
RBX : "rbx",
|
||||
RSP : "rsp",
|
||||
RBP : "rbp",
|
||||
RSI : "rsi",
|
||||
RDI : "rdi",
|
||||
R8 : "r8",
|
||||
R9 : "r9",
|
||||
R10 : "r10",
|
||||
R11 : "r11",
|
||||
R12 : "r12",
|
||||
R13 : "r13",
|
||||
R14 : "r14",
|
||||
R15 : "r15",
|
||||
}
|
||||
|
||||
var knames = [...]string {
|
||||
K0: "k0",
|
||||
K1: "k1",
|
||||
K2: "k2",
|
||||
K3: "k3",
|
||||
K4: "k4",
|
||||
K5: "k5",
|
||||
K6: "k6",
|
||||
K7: "k7",
|
||||
}
|
||||
|
||||
var mmnames = [...]string {
|
||||
MM0: "mm0",
|
||||
MM1: "mm1",
|
||||
MM2: "mm2",
|
||||
MM3: "mm3",
|
||||
MM4: "mm4",
|
||||
MM5: "mm5",
|
||||
MM6: "mm6",
|
||||
MM7: "mm7",
|
||||
}
|
||||
|
||||
var xmmnames = [...]string {
|
||||
XMM0 : "xmm0",
|
||||
XMM1 : "xmm1",
|
||||
XMM2 : "xmm2",
|
||||
XMM3 : "xmm3",
|
||||
XMM4 : "xmm4",
|
||||
XMM5 : "xmm5",
|
||||
XMM6 : "xmm6",
|
||||
XMM7 : "xmm7",
|
||||
XMM8 : "xmm8",
|
||||
XMM9 : "xmm9",
|
||||
XMM10 : "xmm10",
|
||||
XMM11 : "xmm11",
|
||||
XMM12 : "xmm12",
|
||||
XMM13 : "xmm13",
|
||||
XMM14 : "xmm14",
|
||||
XMM15 : "xmm15",
|
||||
XMM16 : "xmm16",
|
||||
XMM17 : "xmm17",
|
||||
XMM18 : "xmm18",
|
||||
XMM19 : "xmm19",
|
||||
XMM20 : "xmm20",
|
||||
XMM21 : "xmm21",
|
||||
XMM22 : "xmm22",
|
||||
XMM23 : "xmm23",
|
||||
XMM24 : "xmm24",
|
||||
XMM25 : "xmm25",
|
||||
XMM26 : "xmm26",
|
||||
XMM27 : "xmm27",
|
||||
XMM28 : "xmm28",
|
||||
XMM29 : "xmm29",
|
||||
XMM30 : "xmm30",
|
||||
XMM31 : "xmm31",
|
||||
}
|
||||
|
||||
var ymmnames = [...]string {
|
||||
YMM0 : "ymm0",
|
||||
YMM1 : "ymm1",
|
||||
YMM2 : "ymm2",
|
||||
YMM3 : "ymm3",
|
||||
YMM4 : "ymm4",
|
||||
YMM5 : "ymm5",
|
||||
YMM6 : "ymm6",
|
||||
YMM7 : "ymm7",
|
||||
YMM8 : "ymm8",
|
||||
YMM9 : "ymm9",
|
||||
YMM10 : "ymm10",
|
||||
YMM11 : "ymm11",
|
||||
YMM12 : "ymm12",
|
||||
YMM13 : "ymm13",
|
||||
YMM14 : "ymm14",
|
||||
YMM15 : "ymm15",
|
||||
YMM16 : "ymm16",
|
||||
YMM17 : "ymm17",
|
||||
YMM18 : "ymm18",
|
||||
YMM19 : "ymm19",
|
||||
YMM20 : "ymm20",
|
||||
YMM21 : "ymm21",
|
||||
YMM22 : "ymm22",
|
||||
YMM23 : "ymm23",
|
||||
YMM24 : "ymm24",
|
||||
YMM25 : "ymm25",
|
||||
YMM26 : "ymm26",
|
||||
YMM27 : "ymm27",
|
||||
YMM28 : "ymm28",
|
||||
YMM29 : "ymm29",
|
||||
YMM30 : "ymm30",
|
||||
YMM31 : "ymm31",
|
||||
}
|
||||
|
||||
var zmmnames = [...]string {
|
||||
ZMM0 : "zmm0",
|
||||
ZMM1 : "zmm1",
|
||||
ZMM2 : "zmm2",
|
||||
ZMM3 : "zmm3",
|
||||
ZMM4 : "zmm4",
|
||||
ZMM5 : "zmm5",
|
||||
ZMM6 : "zmm6",
|
||||
ZMM7 : "zmm7",
|
||||
ZMM8 : "zmm8",
|
||||
ZMM9 : "zmm9",
|
||||
ZMM10 : "zmm10",
|
||||
ZMM11 : "zmm11",
|
||||
ZMM12 : "zmm12",
|
||||
ZMM13 : "zmm13",
|
||||
ZMM14 : "zmm14",
|
||||
ZMM15 : "zmm15",
|
||||
ZMM16 : "zmm16",
|
||||
ZMM17 : "zmm17",
|
||||
ZMM18 : "zmm18",
|
||||
ZMM19 : "zmm19",
|
||||
ZMM20 : "zmm20",
|
||||
ZMM21 : "zmm21",
|
||||
ZMM22 : "zmm22",
|
||||
ZMM23 : "zmm23",
|
||||
ZMM24 : "zmm24",
|
||||
ZMM25 : "zmm25",
|
||||
ZMM26 : "zmm26",
|
||||
ZMM27 : "zmm27",
|
||||
ZMM28 : "zmm28",
|
||||
ZMM29 : "zmm29",
|
||||
ZMM30 : "zmm30",
|
||||
ZMM31 : "zmm31",
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package x86_64
|
||||
|
||||
import (
|
||||
`encoding/binary`
|
||||
`errors`
|
||||
`reflect`
|
||||
`strconv`
|
||||
`unicode/utf8`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
const (
|
||||
_CC_digit = 1 << iota
|
||||
_CC_ident
|
||||
_CC_ident0
|
||||
_CC_number
|
||||
)
|
||||
|
||||
func ispow2(v uint64) bool {
|
||||
return (v & (v - 1)) == 0
|
||||
}
|
||||
|
||||
func isdigit(cc rune) bool {
|
||||
return '0' <= cc && cc <= '9'
|
||||
}
|
||||
|
||||
func isalpha(cc rune) bool {
|
||||
return (cc >= 'a' && cc <= 'z') || (cc >= 'A' && cc <= 'Z')
|
||||
}
|
||||
|
||||
func isident(cc rune) bool {
|
||||
return cc == '_' || isalpha(cc) || isdigit(cc)
|
||||
}
|
||||
|
||||
func isident0(cc rune) bool {
|
||||
return cc == '_' || isalpha(cc)
|
||||
}
|
||||
|
||||
func isnumber(cc rune) bool {
|
||||
return (cc == 'b' || cc == 'B') ||
|
||||
(cc == 'o' || cc == 'O') ||
|
||||
(cc == 'x' || cc == 'X') ||
|
||||
(cc >= '0' && cc <= '9') ||
|
||||
(cc >= 'a' && cc <= 'f') ||
|
||||
(cc >= 'A' && cc <= 'F')
|
||||
}
|
||||
|
||||
func align(v int, n int) int {
|
||||
return (((v - 1) >> n) + 1) << n
|
||||
}
|
||||
|
||||
func append8(m *[]byte, v byte) {
|
||||
*m = append(*m, v)
|
||||
}
|
||||
|
||||
func append16(m *[]byte, v uint16) {
|
||||
p := len(*m)
|
||||
*m = append(*m, 0, 0)
|
||||
binary.LittleEndian.PutUint16((*m)[p:], v)
|
||||
}
|
||||
|
||||
func append32(m *[]byte, v uint32) {
|
||||
p := len(*m)
|
||||
*m = append(*m, 0, 0, 0, 0)
|
||||
binary.LittleEndian.PutUint32((*m)[p:], v)
|
||||
}
|
||||
|
||||
func append64(m *[]byte, v uint64) {
|
||||
p := len(*m)
|
||||
*m = append(*m, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
binary.LittleEndian.PutUint64((*m)[p:], v)
|
||||
}
|
||||
|
||||
func expandmm(m *[]byte, n int, v byte) {
|
||||
sl := (*_GoSlice)(unsafe.Pointer(m))
|
||||
nb := sl.len + n
|
||||
|
||||
/* grow as needed */
|
||||
if nb > cap(*m) {
|
||||
*m = growslice(byteType, *m, nb)
|
||||
}
|
||||
|
||||
/* fill the new area */
|
||||
memset(unsafe.Pointer(uintptr(sl.ptr) + uintptr(sl.len)), v, uintptr(n))
|
||||
sl.len = nb
|
||||
}
|
||||
|
||||
func memset(p unsafe.Pointer, c byte, n uintptr) {
|
||||
if c != 0 {
|
||||
memsetv(p, c, n)
|
||||
} else {
|
||||
memclrNoHeapPointers(p, n)
|
||||
}
|
||||
}
|
||||
|
||||
func memsetv(p unsafe.Pointer, c byte, n uintptr) {
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
*(*byte)(unsafe.Pointer(uintptr(p) + i)) = c
|
||||
}
|
||||
}
|
||||
|
||||
func literal64(v string) (uint64, error) {
|
||||
var nb int
|
||||
var ch rune
|
||||
var ex error
|
||||
var mm [12]byte
|
||||
|
||||
/* unquote the runes */
|
||||
for v != "" {
|
||||
if ch, _, v, ex = strconv.UnquoteChar(v, '\''); ex != nil {
|
||||
return 0, ex
|
||||
} else if nb += utf8.EncodeRune(mm[nb:], ch); nb > 8 {
|
||||
return 0, errors.New("multi-char constant too large")
|
||||
}
|
||||
}
|
||||
|
||||
/* convert to uint64 */
|
||||
return *(*uint64)(unsafe.Pointer(&mm)), nil
|
||||
}
|
||||
|
||||
var (
|
||||
byteWrap = reflect.TypeOf(byte(0))
|
||||
byteType = (*_GoType)(efaceOf(byteWrap).ptr)
|
||||
)
|
||||
|
||||
//go:linkname growslice runtime.growslice
|
||||
func growslice(_ *_GoType, _ []byte, _ int) []byte
|
||||
|
||||
//go:noescape
|
||||
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||
func memclrNoHeapPointers(_ unsafe.Pointer, _ uintptr)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue