mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-27 14:16:39 +00:00
a156188b3e
* update dependencies, bump Go version to 1.19 * bump test image Go version * update golangci-lint * update gotosocial-drone-build * sign * linting, go fmt * update swagger docs * update swagger docs * whitespace * update contributing.md * fuckin whoopsie doopsie * linterino, linteroni * fix followrequest test not starting processor * fix other api/client tests not starting processor * fix remaining tests where processor not started * bump go-runners version * don't check last-webfingered-at, processor may have updated this * update swagger command * update bun to latest version * fix embed to work the same as before with new bun Signed-off-by: kim <grufwub@gmail.com> Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
1027 lines
27 KiB
Go
1027 lines
27 KiB
Go
// Copyright 2019 The CC Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package cc // import "modernc.org/cc/v3"
|
||
|
||
import (
|
||
"encoding/binary"
|
||
"fmt"
|
||
"math"
|
||
"os"
|
||
"runtime"
|
||
|
||
"lukechampine.com/uint128"
|
||
"modernc.org/mathutil"
|
||
)
|
||
|
||
var (
|
||
idAligned = String("aligned")
|
||
idGCCStruct = String("gcc_struct")
|
||
idMSStruct = String("ms_struct")
|
||
idPacked = String("packed")
|
||
|
||
complexTypedefs = map[StringID]Kind{
|
||
dict.sid("__COMPLEX_CHAR_TYPE__"): ComplexChar,
|
||
dict.sid("__COMPLEX_DOUBLE_TYPE__"): ComplexDouble,
|
||
dict.sid("__COMPLEX_FLOAT_TYPE__"): ComplexFloat,
|
||
dict.sid("__COMPLEX_INT_TYPE__"): ComplexInt,
|
||
dict.sid("__COMPLEX_LONG_TYPE__"): ComplexLong,
|
||
dict.sid("__COMPLEX_LONG_DOUBLE_TYPE__"): ComplexLongDouble,
|
||
dict.sid("__COMPLEX_LONG_LONG_TYPE__"): ComplexLongLong,
|
||
dict.sid("__COMPLEX_SHORT_TYPE__"): ComplexShort,
|
||
dict.sid("__COMPLEX_UNSIGNED_TYPE__"): ComplexUInt,
|
||
dict.sid("__COMPLEX_LONG_UNSIGNED_TYPE__"): ComplexULong,
|
||
dict.sid("__COMPLEX_LONG_LONG_UNSIGNED_TYPE__"): ComplexULongLong,
|
||
dict.sid("__COMPLEX_SHORT_UNSIGNED_TYPE__"): ComplexUShort,
|
||
}
|
||
)
|
||
|
||
// NewABI creates an ABI for a given OS and architecture. The OS and architecture values are the same as used in Go.
|
||
// The ABI type map may miss advanced types like complex numbers, etc. If the os/arch pair is not recognized, a
|
||
// *ErrUnsupportedOSArch is returned.
|
||
func NewABI(os, arch string) (ABI, error) {
|
||
order, ok := abiByteOrders[arch]
|
||
if !ok {
|
||
return ABI{}, fmt.Errorf("unsupported arch: %s", arch)
|
||
}
|
||
types, ok := abiTypes[[2]string{os, arch}]
|
||
if !ok {
|
||
return ABI{}, fmt.Errorf("unsupported os/arch pair: %s-%s", os, arch)
|
||
}
|
||
abi := ABI{
|
||
ByteOrder: order,
|
||
Types: make(map[Kind]ABIType, len(types)),
|
||
SignedChar: abiSignedChar[[2]string{os, arch}],
|
||
os: os,
|
||
arch: arch,
|
||
}
|
||
// copy the map, so it can be modified by user
|
||
for k, v := range types {
|
||
abi.Types[k] = v
|
||
}
|
||
return abi, nil
|
||
}
|
||
|
||
// NewABIFromEnv uses GOOS and GOARCH values to create a corresponding ABI.
|
||
// If those environment variables are not set, an OS/arch of a Go runtime is used.
|
||
// It returns a *ErrUnsupportedOSArch if OS/arch pair is not supported.
|
||
func NewABIFromEnv() (ABI, error) {
|
||
osv := os.Getenv("GOOS")
|
||
if osv == "" {
|
||
osv = runtime.GOOS
|
||
}
|
||
arch := os.Getenv("GOARCH")
|
||
if arch == "" {
|
||
arch = runtime.GOARCH
|
||
}
|
||
return NewABI(osv, arch)
|
||
}
|
||
|
||
// ABIType describes properties of a non-aggregate type.
|
||
type ABIType struct {
|
||
Size uintptr
|
||
Align int
|
||
FieldAlign int
|
||
}
|
||
|
||
// ABI describes selected parts of the Application Binary Interface.
|
||
type ABI struct {
|
||
ByteOrder binary.ByteOrder
|
||
Types map[Kind]ABIType
|
||
arch string
|
||
os string
|
||
types map[Kind]Type
|
||
|
||
SignedChar bool
|
||
}
|
||
|
||
func (a *ABI) sanityCheck(ctx *context, intMaxWidth int, s Scope) error {
|
||
if intMaxWidth == 0 {
|
||
intMaxWidth = 64
|
||
}
|
||
|
||
a.types = map[Kind]Type{}
|
||
for _, k := range []Kind{
|
||
Bool,
|
||
Char,
|
||
Double,
|
||
Enum,
|
||
Float,
|
||
Int,
|
||
Long,
|
||
LongDouble,
|
||
LongLong,
|
||
Ptr,
|
||
SChar,
|
||
Short,
|
||
UChar,
|
||
UInt,
|
||
ULong,
|
||
ULongLong,
|
||
UShort,
|
||
Void,
|
||
} {
|
||
v, ok := a.Types[k]
|
||
if !ok {
|
||
if ctx.err(noPos, "ABI is missing %s", k) {
|
||
return ctx.Err()
|
||
}
|
||
|
||
continue
|
||
}
|
||
|
||
if (k != Void && v.Size == 0) || v.Align == 0 || v.FieldAlign == 0 ||
|
||
v.Align > math.MaxUint8 || v.FieldAlign > math.MaxUint8 {
|
||
if ctx.err(noPos, "invalid ABI type %s: %+v", k, v) {
|
||
return ctx.Err()
|
||
}
|
||
}
|
||
|
||
if integerTypes[k] && v.Size > 8 {
|
||
if ctx.err(noPos, "invalid ABI type %s size: %v, must be <= 8", k, v.Size) {
|
||
return ctx.Err()
|
||
}
|
||
}
|
||
var f flag
|
||
if integerTypes[k] && a.isSignedInteger(k) {
|
||
f = fSigned
|
||
}
|
||
t := &typeBase{
|
||
align: byte(a.align(k)),
|
||
fieldAlign: byte(a.fieldAlign(k)),
|
||
flags: f,
|
||
kind: byte(k),
|
||
size: uintptr(a.size(k)),
|
||
}
|
||
a.types[k] = t
|
||
}
|
||
if _, ok := a.Types[Int128]; ok {
|
||
t := &typeBase{
|
||
align: byte(a.align(Int128)),
|
||
fieldAlign: byte(a.fieldAlign(Int128)),
|
||
flags: fSigned,
|
||
kind: byte(Int128),
|
||
size: uintptr(a.size(Int128)),
|
||
}
|
||
a.types[Int128] = t
|
||
}
|
||
if _, ok := a.Types[UInt128]; ok {
|
||
t := &typeBase{
|
||
align: byte(a.align(UInt128)),
|
||
fieldAlign: byte(a.fieldAlign(UInt128)),
|
||
kind: byte(UInt128),
|
||
size: uintptr(a.size(UInt128)),
|
||
}
|
||
a.types[UInt128] = t
|
||
}
|
||
return ctx.Err()
|
||
}
|
||
|
||
func (a *ABI) Type(k Kind) Type { return a.types[k] }
|
||
|
||
func (a *ABI) align(k Kind) int { return a.Types[k].Align }
|
||
func (a *ABI) fieldAlign(k Kind) int { return a.Types[k].FieldAlign }
|
||
func (a *ABI) size(k Kind) int { return int(a.Types[k].Size) }
|
||
|
||
func (a *ABI) isSignedInteger(k Kind) bool {
|
||
if !integerTypes[k] {
|
||
internalError()
|
||
}
|
||
|
||
switch k {
|
||
case Bool, UChar, UInt, ULong, ULongLong, UShort:
|
||
return false
|
||
case Char:
|
||
return a.SignedChar
|
||
default:
|
||
return true
|
||
}
|
||
}
|
||
|
||
func roundup(n, to int64) int64 {
|
||
if r := n % to; r != 0 {
|
||
return n + to - r
|
||
}
|
||
|
||
return n
|
||
}
|
||
|
||
func roundup128(n uint128.Uint128, to uint64) uint128.Uint128 {
|
||
if r := n.Mod(uint128.From64(to)); !r.IsZero() {
|
||
return n.Add64(to).Sub(r)
|
||
}
|
||
|
||
return n
|
||
}
|
||
|
||
func rounddown(n, to int64) int64 {
|
||
return n &^ (to - 1)
|
||
}
|
||
|
||
func rounddown128(n uint128.Uint128, to uint64) uint128.Uint128 {
|
||
return n.And(uint128.Uint128{Hi: ^uint64(0), Lo: ^(to - 1)})
|
||
}
|
||
|
||
func normalizeBitFieldWidth(n byte) byte {
|
||
switch {
|
||
case n <= 8:
|
||
return 8
|
||
case n <= 16:
|
||
return 16
|
||
case n <= 32:
|
||
return 32
|
||
case n <= 64:
|
||
return 64
|
||
default:
|
||
panic(todo("internal error: %v", n))
|
||
}
|
||
}
|
||
|
||
func (a *ABI) layout(ctx *context, n Node, t *structType) *structType {
|
||
if t == nil {
|
||
return nil
|
||
}
|
||
|
||
if t.typeBase.align < 1 {
|
||
t.typeBase.align = 1
|
||
}
|
||
for _, v := range t.attr {
|
||
if _, ok := v.Has(idGCCStruct); ok {
|
||
return a.gccLayout(ctx, n, t)
|
||
}
|
||
|
||
//TODO if _, ok := v.Has(idMSStruct); ok {
|
||
//TODO return a.msLayout(ctx, n, t)
|
||
//TODO }
|
||
}
|
||
|
||
switch {
|
||
case ctx.cfg.Config3.GCCStructs:
|
||
return a.gccLayout(ctx, n, t)
|
||
//TODO case ctx.cfg.Config3.MSStructs:
|
||
//TODO return a.msLayout(ctx, n, t)
|
||
}
|
||
|
||
var hasBitfields bool
|
||
|
||
defer func() {
|
||
if !hasBitfields {
|
||
return
|
||
}
|
||
|
||
m := make(map[uintptr][]*field, len(t.fields))
|
||
for _, f := range t.fields {
|
||
off := f.offset
|
||
m[off] = append(m[off], f)
|
||
}
|
||
for _, s := range m {
|
||
var first *field
|
||
var w byte
|
||
for _, f := range s {
|
||
if first == nil {
|
||
first = f
|
||
}
|
||
if f.isBitField {
|
||
n := f.bitFieldOffset + f.bitFieldWidth
|
||
if n > w {
|
||
w = n
|
||
}
|
||
}
|
||
}
|
||
w = normalizeBitFieldWidth(w)
|
||
for _, f := range s {
|
||
if f.isBitField {
|
||
f.blockStart = first
|
||
f.blockWidth = w
|
||
}
|
||
if a.ByteOrder == binary.BigEndian {
|
||
f.bitFieldOffset = w - f.bitFieldWidth - f.bitFieldOffset
|
||
f.bitFieldMask = (uint64(1)<<f.bitFieldWidth - 1) << f.bitFieldOffset
|
||
}
|
||
}
|
||
}
|
||
}()
|
||
|
||
var off int64 // bit offset
|
||
align := int(t.typeBase.align)
|
||
|
||
switch {
|
||
case t.Kind() == Union:
|
||
for _, f := range t.fields {
|
||
ft := f.Type()
|
||
sz := ft.Size()
|
||
if n := int64(8 * sz); n > off {
|
||
off = n
|
||
}
|
||
al := ft.FieldAlign()
|
||
if al == 0 {
|
||
al = 1
|
||
}
|
||
if al > align {
|
||
align = al
|
||
}
|
||
|
||
if f.isBitField {
|
||
hasBitfields = true
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
}
|
||
f.promote = integerPromotion(a, ft)
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
off = roundup(off, 8*int64(align))
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
default:
|
||
var i int
|
||
var group byte
|
||
var f, lf *field
|
||
for i, f = range t.fields {
|
||
ft := f.Type()
|
||
var sz uintptr
|
||
switch {
|
||
case ft.Kind() == Array && i == len(t.fields)-1:
|
||
if ft.IsIncomplete() || ft.Len() == 0 {
|
||
t.hasFlexibleMember = true
|
||
f.isFlexible = true
|
||
break
|
||
}
|
||
|
||
fallthrough
|
||
default:
|
||
sz = ft.Size()
|
||
}
|
||
|
||
bitSize := 8 * int(sz)
|
||
al := ft.FieldAlign()
|
||
if al == 0 {
|
||
al = 1
|
||
}
|
||
if al > align {
|
||
align = al
|
||
}
|
||
|
||
switch {
|
||
case f.isBitField:
|
||
hasBitfields = true
|
||
eal := 8 * al
|
||
if eal < bitSize {
|
||
eal = bitSize
|
||
}
|
||
down := off &^ (int64(eal) - 1)
|
||
bitoff := off - down
|
||
downMax := off &^ (int64(bitSize) - 1)
|
||
skip := lf != nil && lf.isBitField && lf.bitFieldWidth == 0 ||
|
||
lf != nil && lf.bitFieldWidth == 0 && ctx.cfg.NoFieldAndBitfieldOverlap
|
||
switch {
|
||
case skip || int(off-downMax)+int(f.bitFieldWidth) > bitSize:
|
||
group = 0
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
off += int64(f.bitFieldWidth)
|
||
if f.bitFieldWidth == 0 {
|
||
lf = f
|
||
continue
|
||
}
|
||
default:
|
||
f.offset = uintptr(down >> 3)
|
||
f.bitFieldOffset = byte(bitoff)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << byte(bitoff)
|
||
off += int64(f.bitFieldWidth)
|
||
}
|
||
group += f.bitFieldWidth
|
||
default:
|
||
if n := group % 64; n != 0 {
|
||
if ctx.cfg.FixBitfieldPadding {
|
||
off += int64(normalizeBitFieldWidth(group-n) - group)
|
||
} else {
|
||
group -= n
|
||
off += int64(normalizeBitFieldWidth(group) - group)
|
||
}
|
||
}
|
||
off0 := off
|
||
off = roundup(off, 8*int64(al))
|
||
f.pad = byte(off-off0) >> 3
|
||
f.offset = uintptr(off) >> 3
|
||
off += 8 * int64(sz)
|
||
group = 0
|
||
}
|
||
f.promote = integerPromotion(a, ft)
|
||
lf = f
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
off0 := off
|
||
off = roundup(off, 8*int64(align))
|
||
if f != nil && !f.IsBitField() {
|
||
f.pad = byte(off-off0) >> 3
|
||
}
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
}
|
||
return t
|
||
}
|
||
|
||
func (a *ABI) Ptr(n Node, t Type) Type {
|
||
base := t.base()
|
||
base.align = byte(a.align(Ptr))
|
||
base.fieldAlign = byte(a.fieldAlign(Ptr))
|
||
base.kind = byte(Ptr)
|
||
base.size = uintptr(a.size(Ptr))
|
||
base.flags &^= fIncomplete
|
||
return &pointerType{
|
||
elem: t,
|
||
typeBase: base,
|
||
}
|
||
}
|
||
|
||
func (a *ABI) gccLayout(ctx *context, n Node, t *structType) (r *structType) {
|
||
if t.IsPacked() {
|
||
return a.gccPackedLayout(ctx, n, t)
|
||
}
|
||
|
||
if t.Kind() == Union {
|
||
var off uint128.Uint128 // In bits.
|
||
align := int(t.typeBase.align)
|
||
for _, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
f.offset = 0
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
if uint64(f.bitFieldWidth) > off.Lo {
|
||
off.Lo = uint64(f.bitFieldWidth)
|
||
}
|
||
default:
|
||
al := f.Type().Align()
|
||
if al > align {
|
||
align = al
|
||
}
|
||
f.offset = 0
|
||
off2 := uint128.From64(uint64(f.Type().Size())).Mul64(8)
|
||
if off2.Cmp(off) > 0 {
|
||
off = off2
|
||
}
|
||
}
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
off = roundup128(off, 8*uint64(align))
|
||
t.size = uintptr(off.Rsh(3).Lo)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
var off uint128.Uint128 // In bits.
|
||
align := int(t.typeBase.align)
|
||
for i, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
al := f.Type().Align()
|
||
|
||
// http://jkz.wtf/bit-field-packing-in-gcc-and-clang
|
||
|
||
// 1. Jump backwards to nearest address that would support this type. For
|
||
// example if we have an int jump to the closest address where an int could be
|
||
// stored according to the platform alignment rules.
|
||
down := rounddown128(off, 8*uint64(al))
|
||
|
||
// 2. Get sizeof(current field) bytes from that address.
|
||
alloc := int64(f.Type().Size()) * 8
|
||
need := int64(f.bitFieldWidth)
|
||
if need == 0 && i != 0 {
|
||
off = roundup128(off, 8*uint64(al))
|
||
continue
|
||
}
|
||
|
||
if al > align {
|
||
align = al
|
||
}
|
||
used := int64(off.Sub(down).Lo)
|
||
switch {
|
||
case alloc-used >= need:
|
||
// 3. If the number of bits that we need to store can be stored in these bits,
|
||
// put the bits in the lowest possible bits of this block.
|
||
off = down.Add64(uint64(used))
|
||
f.offset = uintptr(down.Rsh(3).Lo)
|
||
f.bitFieldOffset = byte(used)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << used
|
||
off = off.Add64(uint64(f.bitFieldWidth))
|
||
f.promote = integerPromotion(a, f.Type())
|
||
default:
|
||
// 4. Otherwise, pad the rest of this block with zeros, and store the bits that
|
||
// make up this bit-field in the lowest bits of the next block.
|
||
off = roundup128(off, 8*uint64(al))
|
||
f.offset = uintptr(off.Rsh(3).Lo)
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
off = off.Add64(uint64(f.bitFieldWidth))
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
default:
|
||
al := f.Type().Align()
|
||
if al > align {
|
||
align = al
|
||
}
|
||
off = roundup128(off, 8*uint64(al))
|
||
f.offset = uintptr(off.Rsh(3).Lo)
|
||
sz := uint128.From64(uint64(f.Type().Size()))
|
||
off = off.Add(sz.Mul64(8))
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
var lf *field
|
||
for _, f := range t.fields {
|
||
if lf != nil && !lf.isBitField && !f.isBitField {
|
||
lf.pad = byte(f.offset - lf.offset - lf.Type().Size())
|
||
}
|
||
lf = f
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
off0 := off
|
||
off = roundup128(off, 8*uint64(align))
|
||
if lf != nil && !lf.IsBitField() {
|
||
lf.pad = byte(off.Sub(off0).Rsh(3).Lo)
|
||
}
|
||
t.size = uintptr(off.Rsh(3).Lo)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
func (a *ABI) gccPackedLayout(ctx *context, n Node, t *structType) (r *structType) {
|
||
switch a.arch {
|
||
case "arm", "arm64":
|
||
return a.gccPackedLayoutARM(ctx, n, t)
|
||
}
|
||
|
||
if t.typeBase.flags&fAligned == 0 {
|
||
t.align = 1
|
||
}
|
||
t.fieldAlign = t.align
|
||
if t.Kind() == Union {
|
||
var off int64 // In bits.
|
||
for _, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
panic(todo("%v: ", n.Position()))
|
||
default:
|
||
f.offset = 0
|
||
if off2 := 8 * int64(f.Type().Size()); off2 > off {
|
||
off = off2
|
||
}
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
off = roundup(off, 8)
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
var off int64 // In bits.
|
||
for i, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
if f.bitFieldWidth == 0 {
|
||
if i != 0 {
|
||
off = roundup(off, 8*int64(f.Type().Align()))
|
||
}
|
||
continue
|
||
}
|
||
|
||
if b := f.Type().base(); b.flags&fAligned != 0 {
|
||
off = roundup(off, 8*int64(a.Types[f.Type().Kind()].Align))
|
||
}
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = byte(off & 7)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << f.bitFieldOffset
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
default:
|
||
al := f.Type().Align()
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off) >> 3
|
||
off += 8 * int64(f.Type().Size())
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
var lf *field
|
||
for _, f := range t.fields {
|
||
if lf != nil && !lf.isBitField && !f.isBitField {
|
||
lf.pad = byte(f.offset - lf.offset - lf.Type().Size())
|
||
}
|
||
lf = f
|
||
}
|
||
off0 := off
|
||
off = roundup(off, 8*int64(t.Align()))
|
||
if lf != nil && !lf.IsBitField() {
|
||
lf.pad = byte(off-off0) >> 3
|
||
}
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
func (a *ABI) gccPackedLayoutARM(ctx *context, n Node, t *structType) (r *structType) {
|
||
align := 1
|
||
if t.typeBase.flags&fAligned == 0 {
|
||
t.align = 1
|
||
}
|
||
t.fieldAlign = t.align
|
||
if t.Kind() == Union {
|
||
var off int64 // In bits.
|
||
for _, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
panic(todo("%v: ", n.Position()))
|
||
default:
|
||
f.offset = 0
|
||
if off2 := 8 * int64(f.Type().Size()); off2 > off {
|
||
off = off2
|
||
}
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
off = roundup(off, 8)
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
var off int64 // In bits.
|
||
for i, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
if f.bitFieldWidth == 0 {
|
||
al := f.Type().Align()
|
||
if al > align {
|
||
align = al
|
||
}
|
||
if i != 0 {
|
||
off = roundup(off, 8*int64(f.Type().Align()))
|
||
}
|
||
continue
|
||
}
|
||
|
||
if b := f.Type().base(); b.flags&fAligned != 0 {
|
||
off = roundup(off, 8*int64(a.Types[f.Type().Kind()].Align))
|
||
}
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = byte(off & 7)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << f.bitFieldOffset
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
default:
|
||
al := f.Type().Align()
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off) >> 3
|
||
off += 8 * int64(f.Type().Size())
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
var lf *field
|
||
for _, f := range t.fields {
|
||
if lf != nil && !lf.isBitField && !f.isBitField {
|
||
lf.pad = byte(f.offset - lf.offset - lf.Type().Size())
|
||
}
|
||
lf = f
|
||
}
|
||
if b := t.base(); b.flags&fAligned == 0 {
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
}
|
||
off0 := off
|
||
off = roundup(off, 8*int64(t.Align()))
|
||
if lf != nil && !lf.IsBitField() {
|
||
lf.pad = byte(off-off0) >> 3
|
||
}
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
// https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#x86-Options
|
||
//
|
||
// -mno-ms-bitfields
|
||
//
|
||
// Enable/disable bit-field layout compatible with the native Microsoft Windows
|
||
// compiler.
|
||
//
|
||
// If packed is used on a structure, or if bit-fields are used, it may be that
|
||
// the Microsoft ABI lays out the structure differently than the way GCC
|
||
// normally does. Particularly when moving packed data between functions
|
||
// compiled with GCC and the native Microsoft compiler (either via function
|
||
// call or as data in a file), it may be necessary to access either format.
|
||
//
|
||
// This option is enabled by default for Microsoft Windows targets. This
|
||
// behavior can also be controlled locally by use of variable or type
|
||
// attributes. For more information, see x86 Variable Attributes and x86 Type
|
||
// Attributes.
|
||
//
|
||
// The Microsoft structure layout algorithm is fairly simple with the exception
|
||
// of the bit-field packing. The padding and alignment of members of structures
|
||
// and whether a bit-field can straddle a storage-unit boundary are determine
|
||
// by these rules:
|
||
//
|
||
// Structure members are stored sequentially in the order in which they are
|
||
// declared: the first member has the lowest memory address and the last member
|
||
// the highest. Every data object has an alignment requirement. The alignment
|
||
// requirement for all data except structures, unions, and arrays is either the
|
||
// size of the object or the current packing size (specified with either the
|
||
// aligned attribute or the pack pragma), whichever is less. For structures,
|
||
// unions, and arrays, the alignment requirement is the largest alignment
|
||
// requirement of its members. Every object is allocated an offset so that:
|
||
// offset % alignment_requirement == 0 Adjacent bit-fields are packed into the
|
||
// same 1-, 2-, or 4-byte allocation unit if the integral types are the same
|
||
// size and if the next bit-field fits into the current allocation unit without
|
||
// crossing the boundary imposed by the common alignment requirements of the
|
||
// bit-fields. MSVC interprets zero-length bit-fields in the following ways:
|
||
//
|
||
// If a zero-length bit-field is inserted between two bit-fields that are
|
||
// normally coalesced, the bit-fields are not coalesced. For example:
|
||
//
|
||
// struct
|
||
// {
|
||
// unsigned long bf_1 : 12;
|
||
// unsigned long : 0;
|
||
// unsigned long bf_2 : 12;
|
||
// } t1;
|
||
//
|
||
// The size of t1 is 8 bytes with the zero-length bit-field. If the zero-length
|
||
// bit-field were removed, t1’s size would be 4 bytes.
|
||
//
|
||
// If a zero-length bit-field is inserted after a bit-field, foo, and the
|
||
// alignment of the zero-length bit-field is greater than the member that
|
||
// follows it, bar, bar is aligned as the type of the zero-length bit-field.
|
||
// For example:
|
||
//
|
||
// struct
|
||
// {
|
||
// char foo : 4;
|
||
// short : 0;
|
||
// char bar;
|
||
// } t2;
|
||
//
|
||
// struct
|
||
// {
|
||
// char foo : 4;
|
||
// short : 0;
|
||
// double bar;
|
||
// } t3;
|
||
//
|
||
// For t2, bar is placed at offset 2, rather than offset 1. Accordingly, the
|
||
// size of t2 is 4. For t3, the zero-length bit-field does not affect the
|
||
// alignment of bar or, as a result, the size of the structure.
|
||
//
|
||
// Taking this into account, it is important to note the following:
|
||
//
|
||
// If a zero-length bit-field follows a normal bit-field, the type of the
|
||
// zero-length bit-field may affect the alignment of the structure as whole.
|
||
// For example, t2 has a size of 4 bytes, since the zero-length bit-field
|
||
// follows a normal bit-field, and is of type short. Even if a zero-length
|
||
// bit-field is not followed by a normal bit-field, it may still affect the
|
||
// alignment of the structure:
|
||
//
|
||
// struct
|
||
// {
|
||
// char foo : 6;
|
||
// long : 0;
|
||
// } t4;
|
||
//
|
||
// Here, t4 takes up 4 bytes.
|
||
//
|
||
// Zero-length bit-fields following non-bit-field members are ignored:
|
||
//
|
||
// struct
|
||
// {
|
||
// char foo;
|
||
// long : 0;
|
||
// char bar;
|
||
// } t5;
|
||
//
|
||
// Here, t5 takes up 2 bytes.
|
||
|
||
func (a *ABI) msLayout(ctx *context, n Node, t *structType) (r *structType) {
|
||
if t.IsPacked() {
|
||
return a.msPackedLayout(ctx, n, t)
|
||
}
|
||
|
||
if t.Kind() == Union {
|
||
panic(todo(""))
|
||
}
|
||
|
||
var off int64 // In bits.
|
||
align := int(t.typeBase.align)
|
||
var prev *field
|
||
for i, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
al := f.Type().Align()
|
||
if prev != nil {
|
||
switch {
|
||
case prev.isBitField && prev.Type().Size() != f.Type().Size():
|
||
off = roundup(off, 8*int64(prev.Type().Align()))
|
||
off = roundup(off, 8*int64(al))
|
||
case !prev.isBitField:
|
||
off = roundup(off, 8*int64(al))
|
||
default:
|
||
// Adjacent bit-fields are packed into the same 1-, 2-, or 4-byte allocation
|
||
// unit if the integral types are the same size and if the next bit-field fits
|
||
// into the current allocation unit without crossing the boundary imposed by
|
||
// the common alignment requirements of the bit-fields.
|
||
}
|
||
}
|
||
|
||
// http://jkz.wtf/bit-field-packing-in-gcc-and-clang
|
||
|
||
// 1. Jump backwards to nearest address that would support this type. For
|
||
// example if we have an int jump to the closest address where an int could be
|
||
// stored according to the platform alignment rules.
|
||
down := rounddown(off, 8*int64(al))
|
||
|
||
// 2. Get sizeof(current field) bytes from that address.
|
||
alloc := int64(f.Type().Size()) * 8
|
||
need := int64(f.bitFieldWidth)
|
||
if need == 0 && i != 0 {
|
||
off = roundup(off, 8*int64(al))
|
||
continue
|
||
}
|
||
|
||
if al > align {
|
||
align = al
|
||
}
|
||
used := off - down
|
||
switch {
|
||
case alloc-used >= need:
|
||
// 3. If the number of bits that we need to store can be stored in these bits,
|
||
// put the bits in the lowest possible bits of this block.
|
||
off = down + used
|
||
f.offset = uintptr(down >> 3)
|
||
f.bitFieldOffset = byte(used)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << used
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
default:
|
||
// 4. Otherwise, pad the rest of this block with zeros, and store the bits that
|
||
// make up this bit-field in the lowest bits of the next block.
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
default:
|
||
if prev != nil && prev.isBitField {
|
||
off = roundup(off, 8*int64(prev.Type().Align()))
|
||
}
|
||
al := f.Type().Align()
|
||
if al > align {
|
||
align = al
|
||
}
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off) >> 3
|
||
off += 8 * int64(f.Type().Size())
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
prev = f
|
||
}
|
||
var lf *field
|
||
for _, f := range t.fields {
|
||
if lf != nil && !lf.isBitField && !f.isBitField {
|
||
lf.pad = byte(f.offset - lf.offset - lf.Type().Size())
|
||
}
|
||
lf = f
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
off0 := off
|
||
off = roundup(off, 8*int64(align))
|
||
if lf != nil && !lf.IsBitField() {
|
||
lf.pad = byte(off-off0) >> 3
|
||
}
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
func (a *ABI) msPackedLayout(ctx *context, n Node, t *structType) (r *structType) {
|
||
if t.typeBase.flags&fAligned == 0 {
|
||
t.align = 1
|
||
}
|
||
t.fieldAlign = t.align
|
||
if t.Kind() == Union {
|
||
panic(todo(""))
|
||
var off int64 // In bits.
|
||
for _, f := range t.fields {
|
||
switch {
|
||
case f.isBitField:
|
||
panic(todo("%v: ", n.Position()))
|
||
default:
|
||
f.offset = 0
|
||
if off2 := 8 * int64(f.Type().Size()); off2 > off {
|
||
off = off2
|
||
}
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
}
|
||
off = roundup(off, 8)
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|
||
|
||
var off int64 // In bits.
|
||
var prev *field
|
||
align := int(t.typeBase.align)
|
||
for i, f := range t.fields {
|
||
out:
|
||
switch {
|
||
case f.isBitField:
|
||
al := f.Type().Align()
|
||
switch {
|
||
case prev != nil && prev.IsBitField() && prev.Type().Size() != f.Type().Size():
|
||
off = mathutil.MaxInt64(off, int64(prev.Offset()*8)+int64(prev.BitFieldOffset()+8*prev.Type().Align()))
|
||
off = roundup(off, 8*int64(align))
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
break out
|
||
}
|
||
|
||
// http://jkz.wtf/bit-field-packing-in-gcc-and-clang
|
||
|
||
// 1. Jump backwards to nearest address that would support this type. For
|
||
// example if we have an int jump to the closest address where an int could be
|
||
// stored according to the platform alignment rules.
|
||
down := rounddown(off, 8*int64(al))
|
||
|
||
// 2. Get sizeof(current field) bytes from that address.
|
||
alloc := int64(f.Type().Size()) * 8
|
||
need := int64(f.bitFieldWidth)
|
||
if need == 0 && i != 0 {
|
||
off = roundup(off, 8*int64(al))
|
||
continue
|
||
}
|
||
|
||
used := off - down
|
||
switch {
|
||
case alloc-used >= need:
|
||
// 3. If the number of bits that we need to store can be stored in these bits,
|
||
// put the bits in the lowest possible bits of this block.
|
||
off = down + used
|
||
f.offset = uintptr(down >> 3)
|
||
f.bitFieldOffset = byte(used)
|
||
f.bitFieldMask = (1<<f.bitFieldWidth - 1) << used
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
default:
|
||
// 4. Otherwise, pad the rest of this block with zeros, and store the bits that
|
||
// make up this bit-field in the lowest bits of the next block.
|
||
off = roundup(off, 8*int64(al))
|
||
f.offset = uintptr(off >> 3)
|
||
f.bitFieldOffset = 0
|
||
f.bitFieldMask = 1<<f.bitFieldWidth - 1
|
||
off += int64(f.bitFieldWidth)
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
default:
|
||
off = roundup(off, 8)
|
||
f.offset = uintptr(off) >> 3
|
||
off += 8 * int64(f.Type().Size())
|
||
f.promote = integerPromotion(a, f.Type())
|
||
}
|
||
prev = f
|
||
}
|
||
var lf *field
|
||
for _, f := range t.fields {
|
||
if lf != nil && !lf.isBitField && !f.isBitField {
|
||
lf.pad = byte(f.offset - lf.offset - lf.Type().Size())
|
||
}
|
||
lf = f
|
||
}
|
||
t.align = byte(align)
|
||
t.fieldAlign = byte(align)
|
||
switch {
|
||
case lf != nil && lf.IsBitField():
|
||
off = mathutil.MaxInt64(off, int64(lf.Offset()*8)+int64(lf.BitFieldOffset()+8*lf.Type().Align()))
|
||
off = roundup(off, 8*int64(align))
|
||
default:
|
||
off0 := off
|
||
off = roundup(off, 8*int64(align))
|
||
if lf != nil && !lf.IsBitField() {
|
||
lf.pad = byte(off-off0) >> 3
|
||
}
|
||
}
|
||
t.size = uintptr(off >> 3)
|
||
ctx.structs[StructInfo{Size: t.size, Align: t.Align()}] = struct{}{}
|
||
return t
|
||
}
|