tc_layout.mx raw

   1  package types
   2  
   3  // Type layout for unsafe.Sizeof/Alignof/Offsetof.
   4  //
   5  // The type checker's const evaluator and the SSA builder's const folder both
   6  // need byte sizes, alignments, and struct field offsets. This is the single
   7  // source of truth for both. Layout follows the Go rules for the Moxie type
   8  // set: string/slice are three words, interface and func values are two words,
   9  // map/chan/pointer are one word, arrays and structs are laid out with
  10  // per-field alignment padding.
  11  //
  12  // Words are the TARGET's width, not the host's. Codegen places struct fields
  13  // with LLVM's target data, so a folded unsafe.Sizeof/Offsetof that assumed
  14  // 64-bit pointers would disagree with the addresses the same compiler emits;
  15  // on wasm32 that made unsafe.Sizeof of a two-pointer struct 16 instead of 8
  16  // while the struct itself was 8 bytes.
  17  
  18  import "git.smesh.lol/moxie/pkg/mxutil"
  19  
  20  // ptrSize is the target's pointer width in bytes. Only wasm is 32-bit among
  21  // the targets this compiler emits.
  22  func ptrSize() (n int64) {
  23  	if mxutil.TargetArch() == "wasm" {
  24  		return 4
  25  	}
  26  	return 8
  27  }
  28  
  29  func alignUp(n, a int64) (r int64) {
  30  	if a <= 1 {
  31  		return n
  32  	}
  33  	rem := n % a
  34  	if rem == 0 {
  35  		return n
  36  	}
  37  	return n + (a - rem)
  38  }
  39  
  40  func basicSize(k BasicKind) (sz int64) {
  41  	switch k {
  42  	case Bool, Int8, Uint8:
  43  		return 1
  44  	case Int16, Uint16:
  45  		return 2
  46  	case Int32, Uint32, Float32:
  47  		return 4
  48  	case Int64, Uint64, Float64:
  49  		return 8
  50  	case Uintptr, UnsafePointer:
  51  		return ptrSize()
  52  	case TCString, UntypedString:
  53  		return ptrSize() * 3
  54  	}
  55  	return 8
  56  }
  57  
  58  func basicAlign(k BasicKind) (a int64) {
  59  	switch k {
  60  	case Bool, Int8, Uint8:
  61  		return 1
  62  	case Int16, Uint16:
  63  		return 2
  64  	case Int32, Uint32, Float32:
  65  		return 4
  66  	case Int64, Uint64, Float64:
  67  		// 64-bit scalars stay 8-aligned on the wasm32 target too: the
  68  		// alignment the backend uses for i64 is 8, whatever the pointer
  69  		// width is.
  70  		return 8
  71  	case Uintptr, UnsafePointer, TCString, UntypedString:
  72  		return ptrSize()
  73  	}
  74  	return 8
  75  }
  76  
  77  // TypeSize returns the byte size of t.
  78  func TypeSize(t Type) (sz int64) {
  79  	u := SafeUnderlying(t)
  80  	if u == nil {
  81  		return ptrSize()
  82  	}
  83  	switch v := u.(type) {
  84  	case *Basic:
  85  		return basicSize(v.Kind)
  86  	case *Pointer:
  87  		return ptrSize()
  88  	case *Slice:
  89  		return ptrSize() * 3
  90  	case *Signature:
  91  		return ptrSize() * 2
  92  	case *TCInterface:
  93  		return ptrSize() * 2
  94  	case *TCMap, *TCChan:
  95  		return ptrSize()
  96  	case *Array:
  97  		return TypeSize(v.Elem) * v.Len
  98  	case *TCStruct:
  99  		total := int64(0)
 100  		maxa := int64(1)
 101  		for i := int32(0); i < v.NumFields(); i++ {
 102  			ft := v.Field(i).Typ
 103  			a := TypeAlign(ft)
 104  			if a > maxa {
 105  				maxa = a
 106  			}
 107  			total = alignUp(total, a)
 108  			total += TypeSize(ft)
 109  		}
 110  		return alignUp(total, maxa)
 111  	}
 112  	return ptrSize()
 113  }
 114  
 115  // TypeAlign returns the alignment of t.
 116  func TypeAlign(t Type) (a int64) {
 117  	u := SafeUnderlying(t)
 118  	if u == nil {
 119  		return ptrSize()
 120  	}
 121  	switch v := u.(type) {
 122  	case *Basic:
 123  		return basicAlign(v.Kind)
 124  	case *Array:
 125  		return TypeAlign(v.Elem)
 126  	case *TCStruct:
 127  		a = 1
 128  		for i := int32(0); i < v.NumFields(); i++ {
 129  			fa := TypeAlign(v.Field(i).Typ)
 130  			if fa > a {
 131  				a = fa
 132  			}
 133  		}
 134  		return a
 135  	}
 136  	return ptrSize()
 137  }
 138  
 139  // StructFieldOffset returns the byte offset of field idx in st, following the
 140  // same layout TypeSize uses.
 141  func StructFieldOffset(st *TCStruct, idx int32) (off int64) {
 142  	off = 0
 143  	for i := int32(0); i < idx && i < st.NumFields(); i++ {
 144  		ft := st.Field(i).Typ
 145  		off = alignUp(off, TypeAlign(ft))
 146  		off += TypeSize(ft)
 147  	}
 148  	if idx >= 0 && idx < st.NumFields() {
 149  		off = alignUp(off, TypeAlign(st.Field(idx).Typ))
 150  	}
 151  	return off
 152  }
 153