github.com/aykevl/tinygo@v0.5.0/compiler/sizes.go (about)

     1  package compiler
     2  
     3  import (
     4  	"go/types"
     5  )
     6  
     7  // The code in this file has been copied from
     8  // https://golang.org/src/go/types/sizes.go and modified to allow for int and
     9  // pointer sizes to differ.
    10  // The original license can be found here:
    11  //     https://golang.org/LICENSE
    12  
    13  type StdSizes struct {
    14  	IntSize  int64
    15  	PtrSize  int64
    16  	MaxAlign int64
    17  }
    18  
    19  func (s *StdSizes) Alignof(T types.Type) int64 {
    20  	// For arrays and structs, alignment is defined in terms
    21  	// of alignment of the elements and fields, respectively.
    22  	switch t := T.Underlying().(type) {
    23  	case *types.Array:
    24  		// spec: "For a variable x of array type: unsafe.Alignof(x)
    25  		// is the same as unsafe.Alignof(x[0]), but at least 1."
    26  		return s.Alignof(t.Elem())
    27  	case *types.Struct:
    28  		// spec: "For a variable x of struct type: unsafe.Alignof(x)
    29  		// is the largest of the values unsafe.Alignof(x.f) for each
    30  		// field f of x, but at least 1."
    31  		max := int64(1)
    32  		for i := 0; i < t.NumFields(); i++ {
    33  			f := t.Field(i)
    34  			if a := s.Alignof(f.Type()); a > max {
    35  				max = a
    36  			}
    37  		}
    38  		return max
    39  	case *types.Slice, *types.Interface:
    40  		// Multiword data structures are effectively structs
    41  		// in which each element has size WordSize.
    42  		return s.PtrSize
    43  	case *types.Basic:
    44  		// Strings are like slices and interfaces.
    45  		if t.Info()&types.IsString != 0 {
    46  			return s.PtrSize
    47  		}
    48  	}
    49  	a := s.Sizeof(T) // may be 0
    50  	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
    51  	if a < 1 {
    52  		return 1
    53  	}
    54  	// complex{64,128} are aligned like [2]float{32,64}.
    55  	if t, ok := T.Underlying().(*types.Basic); ok && t.Info()&types.IsComplex != 0 {
    56  		a /= 2
    57  	}
    58  	if a > s.MaxAlign {
    59  		return s.MaxAlign
    60  	}
    61  	return a
    62  }
    63  
    64  func (s *StdSizes) Offsetsof(fields []*types.Var) []int64 {
    65  	offsets := make([]int64, len(fields))
    66  	if len(fields) > 1 && fields[0].Name() == "C union" {
    67  		// This struct contains the magic "C union" field which indicates that
    68  		// this is actually a union from CGo.
    69  		// All fields in the union start at 0 so return that.
    70  		return offsets // all fields are still set to 0
    71  	}
    72  	var o int64
    73  	for i, f := range fields {
    74  		a := s.Alignof(f.Type())
    75  		o = align(o, a)
    76  		offsets[i] = o
    77  		o += s.Sizeof(f.Type())
    78  	}
    79  	return offsets
    80  }
    81  
    82  var basicSizes = [...]byte{
    83  	types.Bool:       1,
    84  	types.Int8:       1,
    85  	types.Int16:      2,
    86  	types.Int32:      4,
    87  	types.Int64:      8,
    88  	types.Uint8:      1,
    89  	types.Uint16:     2,
    90  	types.Uint32:     4,
    91  	types.Uint64:     8,
    92  	types.Float32:    4,
    93  	types.Float64:    8,
    94  	types.Complex64:  8,
    95  	types.Complex128: 16,
    96  }
    97  
    98  func (s *StdSizes) Sizeof(T types.Type) int64 {
    99  	switch t := T.Underlying().(type) {
   100  	case *types.Basic:
   101  		k := t.Kind()
   102  		if int(k) < len(basicSizes) {
   103  			if s := basicSizes[k]; s > 0 {
   104  				return int64(s)
   105  			}
   106  		}
   107  		if k == types.String {
   108  			return s.PtrSize * 2
   109  		}
   110  		if k == types.Int || k == types.Uint {
   111  			return s.IntSize
   112  		}
   113  		if k == types.Uintptr {
   114  			return s.PtrSize
   115  		}
   116  		panic("unknown basic type: " + t.String())
   117  	case *types.Array:
   118  		n := t.Len()
   119  		if n <= 0 {
   120  			return 0
   121  		}
   122  		// n > 0
   123  		a := s.Alignof(t.Elem())
   124  		z := s.Sizeof(t.Elem())
   125  		return align(z, a)*(n-1) + z
   126  	case *types.Slice:
   127  		return s.PtrSize * 3
   128  	case *types.Struct:
   129  		n := t.NumFields()
   130  		if n == 0 {
   131  			return 0
   132  		}
   133  		fields := make([]*types.Var, t.NumFields())
   134  		maxAlign := int64(1)
   135  		for i := range fields {
   136  			field := t.Field(i)
   137  			fields[i] = field
   138  			al := s.Alignof(field.Type())
   139  			if al > maxAlign {
   140  				maxAlign = al
   141  			}
   142  		}
   143  		if fields[0].Name() == "C union" {
   144  			// Magic field that indicates this is a CGo union and not a struct.
   145  			// The size is the biggest element, aligned to the element with the
   146  			// biggest alignment. This is not necessarily the same, for example
   147  			// in the following union:
   148  			//     union { int32_t l; int16_t s[3] }
   149  			maxSize := int64(0)
   150  			for _, field := range fields[1:] {
   151  				si := s.Sizeof(field.Type())
   152  				if si > maxSize {
   153  					maxSize = si
   154  				}
   155  			}
   156  			return align(maxSize, maxAlign)
   157  		} else {
   158  			// This is a regular struct.
   159  			// Pick the size that fits this struct and add some alignment. Some
   160  			// structs have some extra padding at the end which should also be
   161  			// taken care of:
   162  			//     struct { int32 n; byte b }
   163  			offsets := s.Offsetsof(fields)
   164  			return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
   165  		}
   166  	case *types.Interface:
   167  		return s.PtrSize * 2
   168  	case *types.Pointer:
   169  		return s.PtrSize
   170  	default:
   171  		panic("unknown type: " + t.String())
   172  	}
   173  }
   174  
   175  // align returns the smallest y >= x such that y % a == 0.
   176  func align(x, a int64) int64 {
   177  	y := x + a - 1
   178  	return y - y%a
   179  }