github.com/llir/llvm@v0.3.6/asm/inst_memory.go (about)

     1  package asm
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/llir/ll/ast"
     7  	asmenum "github.com/llir/llvm/asm/enum"
     8  	"github.com/llir/llvm/internal/gep"
     9  	"github.com/llir/llvm/ir"
    10  	"github.com/llir/llvm/ir/types"
    11  	"github.com/llir/llvm/ir/value"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // === [ Create IR ] ===========================================================
    16  
    17  // newAllocaInst returns a new IR alloca instruction (without body but with
    18  // type) based on the given AST alloca instruction.
    19  func (fgen *funcGen) newAllocaInst(ident ir.LocalIdent, old *ast.AllocaInst) (*ir.InstAlloca, error) {
    20  	elemType, err := fgen.gen.irType(old.ElemType())
    21  	if err != nil {
    22  		return nil, errors.WithStack(err)
    23  	}
    24  	inst := &ir.InstAlloca{LocalIdent: ident, ElemType: elemType}
    25  	// Cache inst.Typ.
    26  	inst.Type()
    27  	return inst, nil
    28  }
    29  
    30  // newLoadInst returns a new IR load instruction (without body but with type)
    31  // based on the given AST load instruction.
    32  func (fgen *funcGen) newLoadInst(ident ir.LocalIdent, old *ast.LoadInst) (*ir.InstLoad, error) {
    33  	elemType, err := fgen.gen.irType(old.ElemType())
    34  	if err != nil {
    35  		return nil, errors.WithStack(err)
    36  	}
    37  	return &ir.InstLoad{LocalIdent: ident, ElemType: elemType}, nil
    38  }
    39  
    40  // newCmpXchgInst returns a new IR cmpxchg instruction (without body but with
    41  // type) based on the given AST cmpxchg instruction.
    42  func (fgen *funcGen) newCmpXchgInst(ident ir.LocalIdent, old *ast.CmpXchgInst) (*ir.InstCmpXchg, error) {
    43  	oldType, err := fgen.gen.irType(old.New().Typ())
    44  	if err != nil {
    45  		return nil, errors.WithStack(err)
    46  	}
    47  	typ := types.NewStruct(oldType, types.I8)
    48  	return &ir.InstCmpXchg{LocalIdent: ident, Typ: typ}, nil
    49  }
    50  
    51  // newAtomicRMWInst returns a new IR atomicrmw instruction (without body but
    52  // with type) based on the given AST atomicrmw instruction.
    53  func (fgen *funcGen) newAtomicRMWInst(ident ir.LocalIdent, old *ast.AtomicRMWInst) (*ir.InstAtomicRMW, error) {
    54  	dstType, err := fgen.gen.irType(old.Dst().Typ())
    55  	if err != nil {
    56  		return nil, errors.WithStack(err)
    57  	}
    58  	dt, ok := dstType.(*types.PointerType)
    59  	if !ok {
    60  		panic(fmt.Errorf("invalid pointer type; expected *types.PointerType, got %T", dstType))
    61  	}
    62  	return &ir.InstAtomicRMW{LocalIdent: ident, Typ: dt.ElemType}, nil
    63  }
    64  
    65  // newGetElementPtrInst returns a new IR getelementptr instruction (without body
    66  // but with type) based on the given AST getelementptr instruction.
    67  func (fgen *funcGen) newGetElementPtrInst(ident ir.LocalIdent, old *ast.GetElementPtrInst) (*ir.InstGetElementPtr, error) {
    68  	// TODO: handle address space of Src?
    69  	elemType, err := fgen.gen.irType(old.ElemType())
    70  	if err != nil {
    71  		return nil, errors.WithStack(err)
    72  	}
    73  	srcType, err := fgen.gen.irType(old.Src().Typ())
    74  	if err != nil {
    75  		return nil, errors.WithStack(err)
    76  	}
    77  	typ, err := fgen.gen.gepInstType(elemType, srcType, old.Indices())
    78  	if err != nil {
    79  		return nil, errors.WithStack(err)
    80  	}
    81  	return &ir.InstGetElementPtr{LocalIdent: ident, ElemType: elemType, Typ: typ}, nil
    82  }
    83  
    84  // === [ Translate AST to IR ] =================================================
    85  
    86  // --- [ alloca ] --------------------------------------------------------------
    87  
    88  // irAllocaInst translates the given AST alloca instruction into an equivalent
    89  // IR instruction.
    90  func (fgen *funcGen) irAllocaInst(new ir.Instruction, old *ast.AllocaInst) error {
    91  	inst, ok := new.(*ir.InstAlloca)
    92  	if !ok {
    93  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstAlloca, got %T", new))
    94  	}
    95  	// Element type.
    96  	elemType, err := fgen.gen.irType(old.ElemType())
    97  	if err != nil {
    98  		return errors.WithStack(err)
    99  	}
   100  	inst.ElemType = elemType
   101  	// (optional) Number of elements.
   102  	if n, ok := old.NElems(); ok {
   103  		nelems, err := fgen.irTypeValue(n)
   104  		if err != nil {
   105  			return errors.WithStack(err)
   106  		}
   107  		inst.NElems = nelems
   108  	}
   109  	// (optional) In-alloca.
   110  	_, inst.InAlloca = old.InAllocatok()
   111  	// (optional) Swift error.
   112  	_, inst.SwiftError = old.SwiftError()
   113  	// (optional) Alignment.
   114  	if n, ok := old.Align(); ok {
   115  		inst.Align = irAlign(n)
   116  	}
   117  	// (optional) Address space; stored in i.Typ.
   118  	if n, ok := old.AddrSpace(); ok {
   119  		inst.AddrSpace = irAddrSpace(n)
   120  	}
   121  	// (optional) Metadata.
   122  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   123  	if err != nil {
   124  		return errors.WithStack(err)
   125  	}
   126  	inst.Metadata = md
   127  	return nil
   128  }
   129  
   130  // --- [ load ] ----------------------------------------------------------------
   131  
   132  // irLoadInst translates the given AST load instruction into an equivalent IR
   133  // instruction.
   134  func (fgen *funcGen) irLoadInst(new ir.Instruction, old *ast.LoadInst) error {
   135  	inst, ok := new.(*ir.InstLoad)
   136  	if !ok {
   137  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstLoad, got %T", new))
   138  	}
   139  	// Source address.
   140  	src, err := fgen.irTypeValue(old.Src())
   141  	if err != nil {
   142  		return errors.WithStack(err)
   143  	}
   144  	inst.Src = src
   145  	// (optional) Atomic.
   146  	_, inst.Atomic = old.Atomic()
   147  	// (optional) Volatile.
   148  	_, inst.Volatile = old.Volatile()
   149  	// (optional) Sync scope.
   150  	if n, ok := old.SyncScope(); ok {
   151  		inst.SyncScope = stringLit(n.Scope())
   152  	}
   153  	// (optional) Atomic memory ordering constraints.
   154  	if n, ok := old.Ordering(); ok {
   155  		inst.Ordering = asmenum.AtomicOrderingFromString(n.Text())
   156  	}
   157  	// (optional) Alignment.
   158  	if n, ok := old.Align(); ok {
   159  		inst.Align = irAlign(n)
   160  	}
   161  	// (optional) Metadata.
   162  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   163  	if err != nil {
   164  		return errors.WithStack(err)
   165  	}
   166  	inst.Metadata = md
   167  	return nil
   168  }
   169  
   170  // --- [ store ] ---------------------------------------------------------------
   171  
   172  // irStoreInst translates the given AST store instruction into an equivalent IR
   173  // instruction.
   174  func (fgen *funcGen) irStoreInst(new ir.Instruction, old *ast.StoreInst) error {
   175  	inst, ok := new.(*ir.InstStore)
   176  	if !ok {
   177  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstStore, got %T", new))
   178  	}
   179  	// Source value.
   180  	src, err := fgen.irTypeValue(old.Src())
   181  	if err != nil {
   182  		return errors.WithStack(err)
   183  	}
   184  	inst.Src = src
   185  	// Destination address.
   186  	dst, err := fgen.irTypeValue(old.Dst())
   187  	if err != nil {
   188  		return errors.WithStack(err)
   189  	}
   190  	inst.Dst = dst
   191  	// (optional) Atomic.
   192  	_, inst.Atomic = old.Atomic()
   193  	// (optional) Volatile.
   194  	_, inst.Volatile = old.Volatile()
   195  	// (optional) Sync scope.
   196  	if n, ok := old.SyncScope(); ok {
   197  		inst.SyncScope = stringLit(n.Scope())
   198  	}
   199  	// (optional) Atomic memory ordering constraints.
   200  	if n, ok := old.Ordering(); ok {
   201  		inst.Ordering = asmenum.AtomicOrderingFromString(n.Text())
   202  	}
   203  	// (optional) Alignment.
   204  	if n, ok := old.Align(); ok {
   205  		inst.Align = irAlign(n)
   206  	}
   207  	// (optional) Metadata.
   208  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   209  	if err != nil {
   210  		return errors.WithStack(err)
   211  	}
   212  	inst.Metadata = md
   213  	return nil
   214  }
   215  
   216  // --- [ fence ] ---------------------------------------------------------------
   217  
   218  // irFenceInst translates the given AST fence instruction into an equivalent IR
   219  // instruction.
   220  func (fgen *funcGen) irFenceInst(new ir.Instruction, old *ast.FenceInst) error {
   221  	inst, ok := new.(*ir.InstFence)
   222  	if !ok {
   223  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstFence, got %T", new))
   224  	}
   225  	// Atomic memory ordering constraints.
   226  	inst.Ordering = asmenum.AtomicOrderingFromString(old.Ordering().Text())
   227  	// (optional) Sync scope.
   228  	if n, ok := old.SyncScope(); ok {
   229  		inst.SyncScope = stringLit(n.Scope())
   230  	}
   231  	// (optional) Metadata.
   232  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   233  	if err != nil {
   234  		return errors.WithStack(err)
   235  	}
   236  	inst.Metadata = md
   237  	return nil
   238  }
   239  
   240  // --- [ cmpxchg ] -------------------------------------------------------------
   241  
   242  // irCmpXchgInst translates the given AST cmpxchg instruction into an equivalent
   243  // IR instruction.
   244  func (fgen *funcGen) irCmpXchgInst(new ir.Instruction, old *ast.CmpXchgInst) error {
   245  	inst, ok := new.(*ir.InstCmpXchg)
   246  	if !ok {
   247  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstCmpXchg, got %T", new))
   248  	}
   249  	// Address to read from, compare against and store to.
   250  	ptr, err := fgen.irTypeValue(old.Ptr())
   251  	if err != nil {
   252  		return errors.WithStack(err)
   253  	}
   254  	inst.Ptr = ptr
   255  	// Value to compare against.
   256  	cmp, err := fgen.irTypeValue(old.Cmp())
   257  	if err != nil {
   258  		return errors.WithStack(err)
   259  	}
   260  	inst.Cmp = cmp
   261  	// New value to store.
   262  	newValue, err := fgen.irTypeValue(old.New())
   263  	if err != nil {
   264  		return errors.WithStack(err)
   265  	}
   266  	inst.New = newValue
   267  	// Atomic memory ordering constraints on success.
   268  	inst.SuccessOrdering = asmenum.AtomicOrderingFromString(old.SuccessOrdering().Text())
   269  	// Atomic memory ordering constraints on failure.
   270  	inst.FailureOrdering = asmenum.AtomicOrderingFromString(old.FailureOrdering().Text())
   271  	// (optional) Weak.
   272  	_, inst.Weak = old.Weak()
   273  	// (optional) Volatile.
   274  	_, inst.Volatile = old.Volatile()
   275  	// (optional) Sync scope.
   276  	if n, ok := old.SyncScope(); ok {
   277  		inst.SyncScope = stringLit(n.Scope())
   278  	}
   279  	// (optional) Metadata.
   280  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   281  	if err != nil {
   282  		return errors.WithStack(err)
   283  	}
   284  	inst.Metadata = md
   285  	return nil
   286  }
   287  
   288  // --- [ atomicrmw ] -----------------------------------------------------------
   289  
   290  // irAtomicRMWInst translates the given AST atomicrmw instruction into an
   291  // equivalent IR instruction.
   292  func (fgen *funcGen) irAtomicRMWInst(new ir.Instruction, old *ast.AtomicRMWInst) error {
   293  	inst, ok := new.(*ir.InstAtomicRMW)
   294  	if !ok {
   295  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstAtomicRMW, got %T", new))
   296  	}
   297  	// Atomic operation.
   298  	inst.Op = asmenum.AtomicOpFromString(old.Op().Text())
   299  	// Destination address.
   300  	dst, err := fgen.irTypeValue(old.Dst())
   301  	if err != nil {
   302  		return errors.WithStack(err)
   303  	}
   304  	inst.Dst = dst
   305  	// Operand.
   306  	x, err := fgen.irTypeValue(old.X())
   307  	if err != nil {
   308  		return errors.WithStack(err)
   309  	}
   310  	inst.X = x
   311  	// Atomic memory ordering constraints.
   312  	inst.Ordering = asmenum.AtomicOrderingFromString(old.Ordering().Text())
   313  	// (optional) Volatile.
   314  	_, inst.Volatile = old.Volatile()
   315  	// (optional) Sync scope.
   316  	if n, ok := old.SyncScope(); ok {
   317  		inst.SyncScope = stringLit(n.Scope())
   318  	}
   319  	// (optional) Metadata.
   320  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   321  	if err != nil {
   322  		return errors.WithStack(err)
   323  	}
   324  	inst.Metadata = md
   325  	return nil
   326  }
   327  
   328  // --- [ getelementptr ] -------------------------------------------------------
   329  
   330  // irGetElementPtrInst translates the given AST getelementptr instruction into
   331  // an equivalent IR instruction.
   332  func (fgen *funcGen) irGetElementPtrInst(new ir.Instruction, old *ast.GetElementPtrInst) error {
   333  	inst, ok := new.(*ir.InstGetElementPtr)
   334  	if !ok {
   335  		panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstGetElementPtr, got %T", new))
   336  	}
   337  	// Element type; already handled in fgen.newValueInst.
   338  	// Source address.
   339  	src, err := fgen.irTypeValue(old.Src())
   340  	if err != nil {
   341  		return errors.WithStack(err)
   342  	}
   343  	inst.Src = src
   344  	// Element indicies.
   345  	if oldIndices := old.Indices(); len(oldIndices) > 0 {
   346  		inst.Indices = make([]value.Value, len(oldIndices))
   347  		for i, oldIndex := range oldIndices {
   348  			index, err := fgen.irTypeValue(oldIndex)
   349  			if err != nil {
   350  				return errors.WithStack(err)
   351  			}
   352  			inst.Indices[i] = index
   353  		}
   354  	}
   355  	// (optional) In-bounds.
   356  	_, inst.InBounds = old.InBounds()
   357  	// (optional) Metadata.
   358  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   359  	if err != nil {
   360  		return errors.WithStack(err)
   361  	}
   362  	inst.Metadata = md
   363  	return nil
   364  }
   365  
   366  // ### [ Helper functions ] ####################################################
   367  
   368  // gepInstType computes the result type of a getelementptr instruction.
   369  //
   370  //    getelementptr ElemType, Src, Indices
   371  func (gen *generator) gepInstType(elemType, src types.Type, indices []ast.TypeValue) (types.Type, error) {
   372  	var idxs []gep.Index
   373  	for _, index := range indices {
   374  		var idx gep.Index
   375  		if indexVal, ok := index.Val().(ast.Constant); ok {
   376  			idx = gen.getIndex(indexVal)
   377  		} else {
   378  			idx = gep.Index{HasVal: false}
   379  			// Check if index is of vector type.
   380  			indexType, err := gen.irType(index.Typ())
   381  			if err != nil {
   382  				return nil, errors.WithStack(err)
   383  			}
   384  			if indexType, ok := indexType.(*types.VectorType); ok {
   385  				idx.VectorLen = indexType.Len
   386  			}
   387  		}
   388  		idxs = append(idxs, idx)
   389  	}
   390  	return gep.ResultType(elemType, src, idxs), nil
   391  }
   392  
   393  // NOTE: keep getIndex in sync with getIndex in:
   394  //
   395  //    * ast/inst_memory.go
   396  //    * ir/inst_memory.go
   397  //    * ir/constant/expr_memory.go
   398  //
   399  // The reference point and source of truth is in ir/constant/expr_memory.go.
   400  
   401  // getIndex returns the gep index corresponding to the given constant index.
   402  func (gen *generator) getIndex(index ast.Constant) gep.Index {
   403  	switch index := index.(type) {
   404  	case *ast.IntConst:
   405  		// use types.I64 as dummy type for gep indices, the type doesn't matter.
   406  		idx, err := gen.irIntConst(types.I64, index)
   407  		if err != nil {
   408  			panic(fmt.Errorf("unable to parse integer %q; %v", index.Text(), err))
   409  		}
   410  		val := idx.X.Int64()
   411  		return gep.NewIndex(val)
   412  	case *ast.BoolConst:
   413  		if boolLit(index.BoolLit()) {
   414  			return gep.NewIndex(1)
   415  		}
   416  		return gep.NewIndex(0)
   417  	case *ast.ZeroInitializerConst:
   418  		return gep.NewIndex(0)
   419  	case *ast.VectorConst:
   420  		// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
   421  		//
   422  		// > The getelementptr returns a vector of pointers, instead of a single
   423  		// > address, when one or more of its arguments is a vector. In such
   424  		// > cases, all vector arguments should have the same number of elements,
   425  		// > and every scalar argument will be effectively broadcast into a vector
   426  		// > during address calculation.
   427  		elems := index.Elems()
   428  		if len(elems) == 0 {
   429  			return gep.Index{HasVal: false}
   430  		}
   431  		// Sanity check. All vector elements must be integers, and must have the
   432  		// same value.
   433  		var val int64
   434  		for i, elem := range elems {
   435  			switch elem := elem.Val().(type) {
   436  			case *ast.IntConst:
   437  				// use types.I64 as dummy type for gep indices, the type doesn't matter.
   438  				idx, err := gen.irIntConst(types.I64, elem)
   439  				if err != nil {
   440  					panic(fmt.Errorf("unable to parse integer %q; %v", elem.Text(), err))
   441  				}
   442  				x := idx.X.Int64()
   443  				if i == 0 {
   444  					val = x
   445  				} else if x != val {
   446  					// since all elements were not identical, we must conclude that
   447  					// the index vector does not have a concrete value.
   448  					return gep.Index{
   449  						HasVal:    false,
   450  						VectorLen: uint64(len(elems)),
   451  					}
   452  				}
   453  			default:
   454  				// TODO: remove debug output.
   455  				panic(fmt.Errorf("support for gep index vector element type %T not yet implemented", elem))
   456  				//return gep.Index{HasVal: false}
   457  			}
   458  		}
   459  		return gep.Index{
   460  			HasVal:    true,
   461  			Val:       val,
   462  			VectorLen: uint64(len(elems)),
   463  		}
   464  	case *ast.PtrToIntExpr:
   465  		return gep.Index{HasVal: false}
   466  	case *ast.UndefConst:
   467  		return gep.Index{HasVal: false}
   468  	case *ast.PoisonConst:
   469  		return gep.Index{HasVal: false}
   470  	default:
   471  		// TODO: add support for more constant expressions.
   472  		// TODO: remove debug output.
   473  		panic(fmt.Errorf("support for gep index type %T not yet implemented", index))
   474  		//return gep.Index{HasVal: false}
   475  	}
   476  }