github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/gc/align.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gc
     6  
     7  // machine size and rounding alignment is dictated around
     8  // the size of a pointer, set in betypeinit (see ../amd64/galign.go).
     9  var defercalc int
    10  
    11  func Rnd(o int64, r int64) int64 {
    12  	if r < 1 || r > 8 || r&(r-1) != 0 {
    13  		Fatalf("rnd %d", r)
    14  	}
    15  	return (o + r - 1) &^ (r - 1)
    16  }
    17  
    18  func offmod(t *Type) {
    19  	o := int32(0)
    20  	for _, f := range t.Fields().Slice() {
    21  		f.Offset = int64(o)
    22  		o += int32(Widthptr)
    23  		if int64(o) >= Thearch.MAXWIDTH {
    24  			Yyerror("interface too large")
    25  			o = int32(Widthptr)
    26  		}
    27  	}
    28  }
    29  
    30  func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
    31  	starto := o
    32  	maxalign := int32(flag)
    33  	if maxalign < 1 {
    34  		maxalign = 1
    35  	}
    36  	lastzero := int64(0)
    37  	var w int64
    38  	for _, f := range t.Fields().Slice() {
    39  		if f.Type == nil {
    40  			// broken field, just skip it so that other valid fields
    41  			// get a width.
    42  			continue
    43  		}
    44  
    45  		dowidth(f.Type)
    46  		if int32(f.Type.Align) > maxalign {
    47  			maxalign = int32(f.Type.Align)
    48  		}
    49  		if f.Type.Width < 0 {
    50  			Fatalf("invalid width %d", f.Type.Width)
    51  		}
    52  		w = f.Type.Width
    53  		if f.Type.Align > 0 {
    54  			o = Rnd(o, int64(f.Type.Align))
    55  		}
    56  		f.Offset = o
    57  		if f.Nname != nil {
    58  			// this same stackparam logic is in addrescapes
    59  			// in typecheck.go.  usually addrescapes runs after
    60  			// widstruct, in which case we could drop this,
    61  			// but function closure functions are the exception.
    62  			if f.Nname.Name.Param.Stackparam != nil {
    63  				f.Nname.Name.Param.Stackparam.Xoffset = o
    64  				f.Nname.Xoffset = 0
    65  			} else {
    66  				f.Nname.Xoffset = o
    67  			}
    68  		}
    69  
    70  		if w == 0 {
    71  			lastzero = o
    72  		}
    73  		o += w
    74  		if o >= Thearch.MAXWIDTH {
    75  			Yyerror("type %v too large", Tconv(errtype, FmtLong))
    76  			o = 8 // small but nonzero
    77  		}
    78  	}
    79  
    80  	// For nonzero-sized structs which end in a zero-sized thing, we add
    81  	// an extra byte of padding to the type. This padding ensures that
    82  	// taking the address of the zero-sized thing can't manufacture a
    83  	// pointer to the next object in the heap. See issue 9401.
    84  	if flag == 1 && o > starto && o == lastzero {
    85  		o++
    86  	}
    87  
    88  	// final width is rounded
    89  	if flag != 0 {
    90  		o = Rnd(o, int64(maxalign))
    91  	}
    92  	t.Align = uint8(maxalign)
    93  
    94  	// type width only includes back to first field's offset
    95  	t.Width = o - starto
    96  
    97  	return o
    98  }
    99  
   100  func dowidth(t *Type) {
   101  	if Widthptr == 0 {
   102  		Fatalf("dowidth without betypeinit")
   103  	}
   104  
   105  	if t == nil {
   106  		return
   107  	}
   108  
   109  	if t.Width > 0 {
   110  		if t.Align == 0 {
   111  			// See issue 11354
   112  			Fatalf("zero alignment with nonzero size %v", t)
   113  		}
   114  		return
   115  	}
   116  
   117  	if t.Width == -2 {
   118  		if !t.Broke {
   119  			t.Broke = true
   120  			yyerrorl(t.Lineno, "invalid recursive type %v", t)
   121  		}
   122  
   123  		t.Width = 0
   124  		return
   125  	}
   126  
   127  	// break infinite recursion if the broken recursive type
   128  	// is referenced again
   129  	if t.Broke && t.Width == 0 {
   130  		return
   131  	}
   132  
   133  	// defer checkwidth calls until after we're done
   134  	defercalc++
   135  
   136  	lno := lineno
   137  	lineno = t.Lineno
   138  	t.Width = -2
   139  	t.Align = 0
   140  
   141  	et := t.Etype
   142  	switch et {
   143  	case TFUNC, TCHAN, TMAP, TSTRING:
   144  		break
   145  
   146  	// simtype == 0 during bootstrap
   147  	default:
   148  		if Simtype[t.Etype] != 0 {
   149  			et = Simtype[t.Etype]
   150  		}
   151  	}
   152  
   153  	w := int64(0)
   154  	switch et {
   155  	default:
   156  		Fatalf("dowidth: unknown type: %v", t)
   157  
   158  	// compiler-specific stuff
   159  	case TINT8, TUINT8, TBOOL:
   160  		// bool is int8
   161  		w = 1
   162  
   163  	case TINT16, TUINT16:
   164  		w = 2
   165  
   166  	case TINT32, TUINT32, TFLOAT32:
   167  		w = 4
   168  
   169  	case TINT64, TUINT64, TFLOAT64, TCOMPLEX64:
   170  		w = 8
   171  		t.Align = uint8(Widthreg)
   172  
   173  	case TCOMPLEX128:
   174  		w = 16
   175  		t.Align = uint8(Widthreg)
   176  
   177  	case TPTR32:
   178  		w = 4
   179  		checkwidth(t.Elem())
   180  
   181  	case TPTR64:
   182  		w = 8
   183  		checkwidth(t.Elem())
   184  
   185  	case TUNSAFEPTR:
   186  		w = int64(Widthptr)
   187  
   188  	case TINTER: // implemented as 2 pointers
   189  		w = 2 * int64(Widthptr)
   190  
   191  		t.Align = uint8(Widthptr)
   192  		offmod(t)
   193  
   194  	case TCHAN: // implemented as pointer
   195  		w = int64(Widthptr)
   196  
   197  		checkwidth(t.Elem())
   198  
   199  		// make fake type to check later to
   200  		// trigger channel argument check.
   201  		t1 := typWrapper(TCHANARGS, t)
   202  		checkwidth(t1)
   203  
   204  	case TCHANARGS:
   205  		t1 := t.Wrapped()
   206  		dowidth(t1) // just in case
   207  		if t1.Elem().Width >= 1<<16 {
   208  			Yyerror("channel element type too large (>64kB)")
   209  		}
   210  		t.Width = 1
   211  
   212  	case TMAP: // implemented as pointer
   213  		w = int64(Widthptr)
   214  		checkwidth(t.Val())
   215  		checkwidth(t.Key())
   216  
   217  	case TFORW: // should have been filled in
   218  		if !t.Broke {
   219  			Yyerror("invalid recursive type %v", t)
   220  		}
   221  		w = 1 // anything will do
   222  
   223  	// dummy type; should be replaced before use.
   224  	case TANY:
   225  		if Debug['A'] == 0 {
   226  			Fatalf("dowidth any")
   227  		}
   228  		w = 1 // anything will do
   229  
   230  	case TSTRING:
   231  		if sizeof_String == 0 {
   232  			Fatalf("early dowidth string")
   233  		}
   234  		w = int64(sizeof_String)
   235  		t.Align = uint8(Widthptr)
   236  
   237  	case TARRAY:
   238  		if t.Elem() == nil {
   239  			break
   240  		}
   241  		if t.IsArray() {
   242  			dowidth(t.Elem())
   243  			if t.Elem().Width != 0 {
   244  				cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
   245  				if uint64(t.NumElem()) > cap {
   246  					Yyerror("type %v larger than address space", Tconv(t, FmtLong))
   247  				}
   248  			}
   249  
   250  			w = t.NumElem() * t.Elem().Width
   251  			t.Align = t.Elem().Align
   252  		} else if t.IsSlice() {
   253  			w = int64(sizeof_Array)
   254  			checkwidth(t.Elem())
   255  			t.Align = uint8(Widthptr)
   256  		} else if t.isDDDArray() {
   257  			if !t.Broke {
   258  				Yyerror("use of [...] array outside of array literal")
   259  				t.Broke = true
   260  			}
   261  		} else {
   262  			Fatalf("dowidth %v", t) // probably [...]T
   263  		}
   264  
   265  	case TSTRUCT:
   266  		if t.Funarg {
   267  			Fatalf("dowidth fn struct %v", t)
   268  		}
   269  		w = widstruct(t, t, 0, 1)
   270  
   271  	// make fake type to check later to
   272  	// trigger function argument computation.
   273  	case TFUNC:
   274  		t1 := typWrapper(TFUNCARGS, t)
   275  		checkwidth(t1)
   276  		w = int64(Widthptr) // width of func type is pointer
   277  
   278  	// function is 3 cated structures;
   279  	// compute their widths as side-effect.
   280  	case TFUNCARGS:
   281  		t1 := t.Wrapped()
   282  		w = widstruct(t1, t1.Recvs(), 0, 0)
   283  		w = widstruct(t1, t1.Params(), w, Widthreg)
   284  		w = widstruct(t1, t1.Results(), w, Widthreg)
   285  		t1.Argwid = w
   286  		if w%int64(Widthreg) != 0 {
   287  			Warn("bad type %v %d\n", t1, w)
   288  		}
   289  		t.Align = 1
   290  	}
   291  
   292  	if Widthptr == 4 && w != int64(int32(w)) {
   293  		Yyerror("type %v too large", t)
   294  	}
   295  
   296  	t.Width = w
   297  	if t.Align == 0 {
   298  		if w > 8 || w&(w-1) != 0 {
   299  			Fatalf("invalid alignment for %v", t)
   300  		}
   301  		t.Align = uint8(w)
   302  	}
   303  
   304  	lineno = lno
   305  
   306  	if defercalc == 1 {
   307  		resumecheckwidth()
   308  	} else {
   309  		defercalc--
   310  	}
   311  }
   312  
   313  // when a type's width should be known, we call checkwidth
   314  // to compute it.  during a declaration like
   315  //
   316  //	type T *struct { next T }
   317  //
   318  // it is necessary to defer the calculation of the struct width
   319  // until after T has been initialized to be a pointer to that struct.
   320  // similarly, during import processing structs may be used
   321  // before their definition.  in those situations, calling
   322  // defercheckwidth() stops width calculations until
   323  // resumecheckwidth() is called, at which point all the
   324  // checkwidths that were deferred are executed.
   325  // dowidth should only be called when the type's size
   326  // is needed immediately.  checkwidth makes sure the
   327  // size is evaluated eventually.
   328  
   329  var deferredTypeStack []*Type
   330  
   331  func checkwidth(t *Type) {
   332  	if t == nil {
   333  		return
   334  	}
   335  
   336  	// function arg structs should not be checked
   337  	// outside of the enclosing function.
   338  	if t.Funarg {
   339  		Fatalf("checkwidth %v", t)
   340  	}
   341  
   342  	if defercalc == 0 {
   343  		dowidth(t)
   344  		return
   345  	}
   346  
   347  	if t.Deferwidth {
   348  		return
   349  	}
   350  	t.Deferwidth = true
   351  
   352  	deferredTypeStack = append(deferredTypeStack, t)
   353  }
   354  
   355  func defercheckwidth() {
   356  	// we get out of sync on syntax errors, so don't be pedantic.
   357  	if defercalc != 0 && nerrors == 0 {
   358  		Fatalf("defercheckwidth")
   359  	}
   360  	defercalc = 1
   361  }
   362  
   363  func resumecheckwidth() {
   364  	if defercalc == 0 {
   365  		Fatalf("resumecheckwidth")
   366  	}
   367  	for len(deferredTypeStack) > 0 {
   368  		t := deferredTypeStack[len(deferredTypeStack)-1]
   369  		deferredTypeStack = deferredTypeStack[:len(deferredTypeStack)-1]
   370  		t.Deferwidth = false
   371  		dowidth(t)
   372  	}
   373  
   374  	defercalc = 0
   375  }
   376  
   377  // compute total size of f's in/out arguments.
   378  func Argsize(t *Type) int {
   379  	var w int64
   380  
   381  	for _, p := range recvsParamsResults {
   382  		for _, f := range p(t).Fields().Slice() {
   383  			if x := f.End(); x > w {
   384  				w = x
   385  			}
   386  		}
   387  	}
   388  
   389  	w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1)
   390  	if int64(int(w)) != w {
   391  		Fatalf("argsize too big")
   392  	}
   393  	return int(w)
   394  }