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

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/blakesmith/ar"
    14  )
    15  
    16  // These are the GENERIC_SOURCES according to CMakeList.txt.
    17  var genericBuiltins = []string{
    18  	"absvdi2.c",
    19  	"absvsi2.c",
    20  	"absvti2.c",
    21  	"adddf3.c",
    22  	"addsf3.c",
    23  	"addtf3.c",
    24  	"addvdi3.c",
    25  	"addvsi3.c",
    26  	"addvti3.c",
    27  	"apple_versioning.c",
    28  	"ashldi3.c",
    29  	"ashlti3.c",
    30  	"ashrdi3.c",
    31  	"ashrti3.c",
    32  	"bswapdi2.c",
    33  	"bswapsi2.c",
    34  	"clzdi2.c",
    35  	"clzsi2.c",
    36  	"clzti2.c",
    37  	"cmpdi2.c",
    38  	"cmpti2.c",
    39  	"comparedf2.c",
    40  	"comparesf2.c",
    41  	"ctzdi2.c",
    42  	"ctzsi2.c",
    43  	"ctzti2.c",
    44  	"divdc3.c",
    45  	"divdf3.c",
    46  	"divdi3.c",
    47  	"divmoddi4.c",
    48  	"divmodsi4.c",
    49  	"divsc3.c",
    50  	"divsf3.c",
    51  	"divsi3.c",
    52  	"divtc3.c",
    53  	"divti3.c",
    54  	"divtf3.c",
    55  	"extendsfdf2.c",
    56  	"extendhfsf2.c",
    57  	"ffsdi2.c",
    58  	"ffssi2.c",
    59  	"ffsti2.c",
    60  	"fixdfdi.c",
    61  	"fixdfsi.c",
    62  	"fixdfti.c",
    63  	"fixsfdi.c",
    64  	"fixsfsi.c",
    65  	"fixsfti.c",
    66  	"fixunsdfdi.c",
    67  	"fixunsdfsi.c",
    68  	"fixunsdfti.c",
    69  	"fixunssfdi.c",
    70  	"fixunssfsi.c",
    71  	"fixunssfti.c",
    72  	"floatdidf.c",
    73  	"floatdisf.c",
    74  	"floatsidf.c",
    75  	"floatsisf.c",
    76  	"floattidf.c",
    77  	"floattisf.c",
    78  	"floatundidf.c",
    79  	"floatundisf.c",
    80  	"floatunsidf.c",
    81  	"floatunsisf.c",
    82  	"floatuntidf.c",
    83  	"floatuntisf.c",
    84  	//"int_util.c",
    85  	"lshrdi3.c",
    86  	"lshrti3.c",
    87  	"moddi3.c",
    88  	"modsi3.c",
    89  	"modti3.c",
    90  	"muldc3.c",
    91  	"muldf3.c",
    92  	"muldi3.c",
    93  	"mulodi4.c",
    94  	"mulosi4.c",
    95  	"muloti4.c",
    96  	"mulsc3.c",
    97  	"mulsf3.c",
    98  	"multi3.c",
    99  	"multf3.c",
   100  	"mulvdi3.c",
   101  	"mulvsi3.c",
   102  	"mulvti3.c",
   103  	"negdf2.c",
   104  	"negdi2.c",
   105  	"negsf2.c",
   106  	"negti2.c",
   107  	"negvdi2.c",
   108  	"negvsi2.c",
   109  	"negvti2.c",
   110  	"os_version_check.c",
   111  	"paritydi2.c",
   112  	"paritysi2.c",
   113  	"parityti2.c",
   114  	"popcountdi2.c",
   115  	"popcountsi2.c",
   116  	"popcountti2.c",
   117  	"powidf2.c",
   118  	"powisf2.c",
   119  	"powitf2.c",
   120  	"subdf3.c",
   121  	"subsf3.c",
   122  	"subvdi3.c",
   123  	"subvsi3.c",
   124  	"subvti3.c",
   125  	"subtf3.c",
   126  	"trampoline_setup.c",
   127  	"truncdfhf2.c",
   128  	"truncdfsf2.c",
   129  	"truncsfhf2.c",
   130  	"ucmpdi2.c",
   131  	"ucmpti2.c",
   132  	"udivdi3.c",
   133  	"udivmoddi4.c",
   134  	"udivmodsi4.c",
   135  	"udivmodti4.c",
   136  	"udivsi3.c",
   137  	"udivti3.c",
   138  	"umoddi3.c",
   139  	"umodsi3.c",
   140  	"umodti3.c",
   141  }
   142  
   143  var aeabiBuiltins = []string{
   144  	"arm/aeabi_cdcmp.S",
   145  	"arm/aeabi_cdcmpeq_check_nan.c",
   146  	"arm/aeabi_cfcmp.S",
   147  	"arm/aeabi_cfcmpeq_check_nan.c",
   148  	"arm/aeabi_dcmp.S",
   149  	"arm/aeabi_div0.c",
   150  	"arm/aeabi_drsub.c",
   151  	"arm/aeabi_fcmp.S",
   152  	"arm/aeabi_frsub.c",
   153  	"arm/aeabi_idivmod.S",
   154  	"arm/aeabi_ldivmod.S",
   155  	"arm/aeabi_memcmp.S",
   156  	"arm/aeabi_memcpy.S",
   157  	"arm/aeabi_memmove.S",
   158  	"arm/aeabi_memset.S",
   159  	"arm/aeabi_uidivmod.S",
   160  	"arm/aeabi_uldivmod.S",
   161  }
   162  
   163  func builtinFiles(target string) []string {
   164  	builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
   165  	if strings.HasPrefix(target, "arm") {
   166  		builtins = append(builtins, aeabiBuiltins...)
   167  	}
   168  	return builtins
   169  }
   170  
   171  // builtinsDir returns the directory where the sources for compiler-rt are kept.
   172  func builtinsDir() string {
   173  	return filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins")
   174  }
   175  
   176  // Get the builtins archive, possibly generating it as needed.
   177  func loadBuiltins(target string) (path string, err error) {
   178  	// Try to load a precompiled compiler-rt library.
   179  	precompiledPath := filepath.Join(sourceDir(), "pkg", target, "compiler-rt.a")
   180  	if _, err := os.Stat(precompiledPath); err == nil {
   181  		// Found a precompiled compiler-rt for this OS/architecture. Return the
   182  		// path directly.
   183  		return precompiledPath, nil
   184  	}
   185  
   186  	outfile := "librt-" + target + ".a"
   187  	builtinsDir := builtinsDir()
   188  
   189  	builtins := builtinFiles(target)
   190  	srcs := make([]string, len(builtins))
   191  	for i, name := range builtins {
   192  		srcs[i] = filepath.Join(builtinsDir, name)
   193  	}
   194  
   195  	if path, err := cacheLoad(outfile, commands["clang"], srcs); path != "" || err != nil {
   196  		return path, err
   197  	}
   198  
   199  	var cachepath string
   200  	err = compileBuiltins(target, func(path string) error {
   201  		path, err := cacheStore(path, outfile, commands["clang"], srcs)
   202  		cachepath = path
   203  		return err
   204  	})
   205  	return cachepath, err
   206  }
   207  
   208  // compileBuiltins compiles builtins from compiler-rt into a static library.
   209  // When it succeeds, it will call the callback with the resulting path. The path
   210  // will be removed after callback returns. If callback returns an error, this is
   211  // passed through to the return value of this function.
   212  func compileBuiltins(target string, callback func(path string) error) error {
   213  	builtinsDir := builtinsDir()
   214  
   215  	builtins := builtinFiles(target)
   216  	srcs := make([]string, len(builtins))
   217  	for i, name := range builtins {
   218  		srcs[i] = filepath.Join(builtinsDir, name)
   219  	}
   220  
   221  	dirPrefix := "tinygo-builtins"
   222  	remapDir := filepath.Join(os.TempDir(), dirPrefix)
   223  	dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	defer os.RemoveAll(dir)
   228  
   229  	// Compile all builtins.
   230  	// TODO: use builtins optimized for a given target if available.
   231  	objs := make([]string, 0, len(builtins))
   232  	for _, name := range builtins {
   233  		objname := name
   234  		if strings.LastIndexByte(objname, '/') >= 0 {
   235  			objname = objname[strings.LastIndexByte(objname, '/'):]
   236  		}
   237  		objpath := filepath.Join(dir, objname+".o")
   238  		objs = append(objs, objpath)
   239  		srcpath := filepath.Join(builtinsDir, name)
   240  		// Note: -fdebug-prefix-map is necessary to make the output archive
   241  		// reproducible. Otherwise the temporary directory is stored in the
   242  		// archive itself, which varies each run.
   243  		cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
   244  		cmd.Stdout = os.Stdout
   245  		cmd.Stderr = os.Stderr
   246  		cmd.Dir = dir
   247  		err = cmd.Run()
   248  		if err != nil {
   249  			return &commandError{"failed to build", srcpath, err}
   250  		}
   251  	}
   252  
   253  	// Put all builtins in an archive to link as a static library.
   254  	// Note: this does not create a symbol index, but ld.lld doesn't seem to
   255  	// care.
   256  	arpath := filepath.Join(dir, "librt.a")
   257  	arfile, err := os.Create(arpath)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	defer arfile.Close()
   262  	arwriter := ar.NewWriter(arfile)
   263  	err = arwriter.WriteGlobalHeader()
   264  	if err != nil {
   265  		return &os.PathError{"write ar header", arpath, err}
   266  	}
   267  	for _, objpath := range objs {
   268  		name := filepath.Base(objpath)
   269  		objfile, err := os.Open(objpath)
   270  		if err != nil {
   271  			return err
   272  		}
   273  		defer objfile.Close()
   274  		st, err := objfile.Stat()
   275  		if err != nil {
   276  			return err
   277  		}
   278  		arwriter.WriteHeader(&ar.Header{
   279  			Name:    name,
   280  			ModTime: time.Unix(0, 0),
   281  			Uid:     0,
   282  			Gid:     0,
   283  			Mode:    0644,
   284  			Size:    st.Size(),
   285  		})
   286  		n, err := io.Copy(arwriter, objfile)
   287  		if err != nil {
   288  			return err
   289  		}
   290  		if n != st.Size() {
   291  			return errors.New("file modified during ar creation: " + arpath)
   292  		}
   293  	}
   294  
   295  	// Give the caller the resulting file. The callback must copy the file,
   296  	// because after it returns the temporary directory will be removed.
   297  	return callback(arpath)
   298  }