Changeset 2464

Show
Ignore:
Timestamp:
12/10/13 04:24:24 (10 years ago)
Author:
jwoithe
Message:

SConstruct: merge extensive CPU flag detection changes from Franklyn Berry. The most significant improvement is that it permits a correct build when running 32-bit userspace on a 64-bit kernel (common for powerpc64 but can also happen for x86 via the x32 ABI). Testing is encouraged to ensure that this introduces no regressions. Please report breakages on ffado-devel.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/libffado/SConstruct

    r2463 r2464  
    489489needs_fPIC = False 
    490490 
     491#=== Begin Revised CXXFLAGS ========================================= 
     492def outputof(*cmd): 
     493    """Run a command without running a shell, return cmd's stdout 
     494    """ 
     495    p = Popen(cmd, stdout=PIPE) 
     496    return p.communicate()[0] 
     497 
     498def cpuinfo_kv(): 
     499    """generator which reads lines from Linux /proc/cpuinfo and splits them 
     500    into key:value tokens and yields (key, value) tuple. 
     501    """ 
     502    f = open('/proc/cpuinfo', 'r') 
     503    for line in f: 
     504        line = line.strip() 
     505        if line: 
     506            k,v = line.split(':') 
     507            yield (k.strip(), v.strip()) 
     508    f.close() 
     509 
     510 
     511class CpuInfo (object): 
     512    """Collects information about the CPU, mainly from /proc/cpuinfo 
     513    """ 
     514    def __init__(self): 
     515        self.sysname, self.hostname, self.release, self.version, self.machine = os.uname() 
     516        # general CPU architecture 
     517        self.is_x86 = self.machine in ('i686', 'x86_64') or \ 
     518                      re.match("i[3-5]86", self.machine) or False 
     519        self.is_powerpc = self.machine in ('ppc64', 'ppc', 'powerpc', 'powerpc64') 
     520        #!!! probably not comprehensive 
     521        self.is_mips = self.machine == 'mips' 
     522        #!!! not a comprehensive list. uname -m on one android phone reports 'armv71' 
     523        # I have no other arm devices I can check 
     524        self.is_arm = self.machine in ('armv71', ) 
     525 
     526        self.cpu_count = 0 
     527        if self.is_x86: 
     528            self.cpu_info_x86() 
     529        elif self.is_powerpc: 
     530            self.cpu_info_ppc() 
     531        elif self.is_mips: 
     532            self.cpu_info_mips() 
     533 
     534        # 64-bit (x86_64/AMD64/Intel64) 
     535        # Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable) 
     536        self.is_64bit = (self.is_x86 and 'lm' in self.x86_flags) or \ 
     537                        (self.is_powerpc and '970' in self.ppc_type) 
     538 
     539        # Hardware virtualization capable: vmx (Intel), svm (AMD) 
     540        self.has_hwvirt = self.is_x86 and ( 
     541                            (self.is_amd and 'svm' in self.x86_flags) or 
     542                            (self.is_intel and 'vmx' in self.x86_flags)) 
     543 
     544        # Physical Address Extensions (support for more than 4GB of RAM) 
     545        self.has_pae = self.is_x86 and 'pae' in self.x86_flags 
     546 
     547 
     548    def cpu_info_x86(self): 
     549        "parse /proc/cpuinfo for x86 kernels" 
     550        for k,v in cpuinfo_kv(): 
     551            if k == 'processor': 
     552                self.cpu_count += 1 
     553                if self.cpu_count > 1: 
     554                    # assume all CPUs are identical features, no need to 
     555                    # parse all of them 
     556                    continue 
     557            elif k == 'vendor_id': # AuthenticAMD, GenuineIntel 
     558                self.vendor_id = v 
     559                self.is_amd = v == 'AuthenticAMD' 
     560                self.is_intel = v == 'GenuineIntel' 
     561            elif k == 'flags': 
     562                self.x86_flags = v.split() 
     563            elif k == 'model name': 
     564                self.model_name = v 
     565            elif k == 'cpu family': 
     566                self.cpu_family = v 
     567            elif k == 'model': 
     568                self.model = v 
     569 
     570    def cpu_info_ppc(self): 
     571        "parse /proc/cpuinfo for PowerPC kernels" 
     572        # http://en.wikipedia.org/wiki/List_of_PowerPC_processors 
     573        # PowerPC 7xx family 
     574        # PowerPC 740 and 750, 233-366 MHz 
     575        # 745/755, 300–466 MHz 
     576 
     577        # PowerPC G4 series 
     578        # 7400/7410 350 - 550 MHz, uses AltiVec, a SIMD extension of the original PPC specs 
     579        # 7450 micro-architecture family up to 1.5 GHz and 256 kB on-chip L2 cache and improved Altivec 
     580        # 7447/7457 micro-architecture family up to 1.8 GHz with 512 kB on-chip L2 cache 
     581        # 7448 micro-architecture family (1.5 GHz) in 90 nm with 1MB L2 cache and slightly 
     582        #  improved AltiVec (out of order instructions). 
     583        # 8640/8641/8640D/8641D with one or two e600 (Formerly known as G4) cores, 1MB L2 cache 
     584 
     585        # PowerPC G5 series 
     586        # 970 (2003), 64-bit, derived from POWER4, enhanced with VMX, 512 kB L2 cache, 1.4 – 2 GHz 
     587        # 970FX (2004), manufactured at 90 nm, 1.8 - 2.7 GHz 
     588        # 970GX (2006), manufactured at 90 nm, 1MB L2 cache/core, 1.2 - 2.5 GHz 
     589        # 970MP (2005), dual core, 1 MB L2 cache/core, 1.6 - 2.5 GHz 
     590        for k,v in cpuinfo_kv(): 
     591            if k == 'processor': 
     592                self.cpu_count += 1 
     593            elif k == 'cpu': 
     594                self.is_altivec_supported = 'altivec' in v 
     595                ppc_type, x = v.split(',') 
     596                self.ppc_type = ppc_type.strip() 
     597        # older kernels might not have a 'processor' line 
     598        if self.cpu_count == 0: 
     599            self.cpu_count += 1 
     600 
     601 
     602    def cpu_info_mips(self): 
     603        "parse /proc/cpuinfo for MIPS kernels" 
     604        for k,v in cpuinfo_kv(): 
     605            if k == 'processor': 
     606                self.cpu_count += 1 
     607            elif k == 'cpu model': 
     608                self.mips_cpu_model = v 
     609 
     610 
     611def is_userspace_32bit(cpuinfo): 
     612    """Even if `uname -m` reports a 64-bit architecture, userspace could still 
     613    be 32-bit, such as Debian on powerpc64. This function tries to figure out 
     614    if userspace is 32-bit, i.e. we might need to pass '-m32' or '-m64' to gcc. 
     615    """ 
     616    if not cpuinfo.is_64bit: 
     617        return True 
     618    # note that having a 64-bit CPU means nothing for these purposes. You could 
     619    # run a completely 32-bit system on a 64-bit capable CPU. 
     620    answer = None 
     621    # Debian ppc64 returns machine 'ppc64', but userspace might be 32-bit 
     622    # We'll make an educated guess by examining a known executable 
     623    exe = '/bin/mount' 
     624    if os.path.isfile(exe): 
     625        #print 'Found %s' % exe 
     626        if os.path.islink(exe): 
     627            real_exe = os.path.join(os.path.dirname(exe), os.readlink(exe)) 
     628            #print '%s is a symlink to %s' % (exe, real_exe) 
     629        else: 
     630            real_exe = exe 
     631        # presumably if a person is running this script, they should have 
     632        # a gcc toolchain installed... 
     633        x = outputof('objdump', '-Wi', real_exe) 
     634        # should emit a line that looks like this: 
     635        # /bin/mount:     file format elf32-i386 
     636        # or like this: 
     637        # /bin/mount:     file format elf64-x86-64 
     638        # or like this: 
     639        # /bin/mount:     file format elf32-powerpc 
     640        for line in x.split('\n'): 
     641            line = line.strip() 
     642            if line.startswith(real_exe) and 'file format' in line: 
     643                x, fmt = line.rsplit(None, 1) 
     644                answer = 'elf32' in fmt 
     645                break 
     646    else: 
     647        print '!!! Not found %s' % exe 
     648    return answer 
     649 
     650 
     651def cc_flags_x86(cpuinfo, enable_optimizations): 
     652    """add certain gcc -m flags based on CPU features 
     653    """ 
     654    # See http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/i386-and-x86_002d64-Options.html 
     655    cc_opts = [] 
     656    if cpuinfo.machine == 'i586': 
     657        cc_opts.append('-march=i586') 
     658    elif cpuinfo.machine == 'i686': 
     659        cc_opts.append('-march=i686') 
     660 
     661    if 'mmx' in cpuinfo.x86_flags: 
     662        cc_opts.append('-mmmx') 
     663 
     664    # map from proc/cpuinfo flags to gcc options 
     665    opt_flags = [ 
     666            ('sse', ('-mfpmath=sse', '-msse')), 
     667            ('sse2', '-msse2'), 
     668            ('ssse3', '-mssse3'), 
     669            ('sse4', '-msse4'), 
     670            ('sse4_1', '-msse4.1'), 
     671            ('sse4_2', '-msse4.2'), 
     672            ('sse4a', '-msse4a'), 
     673            ('3dnow', '-m3dnow'), 
     674    ] 
     675    if enable_optimizations: 
     676        for flag, gccopt in opt_flags: 
     677            if flag in cpuinfo.x86_flags: 
     678                if isinstance(gccopt, (tuple, list)): 
     679                    cc_opts.extend(gccopt) 
     680                else: 
     681                    cc_opts.append(gccopt) 
     682    return cc_opts 
     683 
     684 
     685def cc_flags_powerpc(cpuinfo, enable_optimizations): 
     686    """add certain gcc -m flags based on CPU model 
     687    """ 
     688    cc_opts = [] 
     689    if cpuinfo.is_altivec_supported: 
     690        cc_opts.append ('-maltivec') 
     691        cc_opts.append ('-mabi=altivec') 
     692 
     693    if re.match('74[0145][0578]A?', cpuinfo.ppc_type) is not None: 
     694        cc_opts.append ('-mcpu=7400') 
     695        cc_opts.append ('-mtune=7400') 
     696    elif re.match('750', cpuinfo.ppc_type) is not None: 
     697        cc_opts.append ('-mcpu=750') 
     698        cc_opts.append ('-mtune=750') 
     699    elif re.match('PPC970', cpuinfo.ppc_type) is not None: 
     700        cc_opts.append ('-mcpu=970') 
     701        cc_opts.append ('-mtune=970') 
     702    elif re.match('Cell Broadband Engine', cpuinfo.ppc_type) is not None: 
     703        cc_opts.append('-mcpu=cell') 
     704        cc_opts.append('-mtune=cell') 
     705    return cc_opts 
     706#=== End Revised CXXFLAGS ========================================= 
     707 
    491708# Autodetect 
    492709if env['DIST_TARGET'] == 'auto': 
    493     if re.search ("x86_64", config[config_cpu]) != None: 
     710    if re.search ("x86_64", config[config_cpu]) is not None: 
    494711        env['DIST_TARGET'] = 'x86_64' 
    495     elif re.search("i[0-5]86", config[config_cpu]) != None: 
     712    elif re.search("i[0-5]86", config[config_cpu]) is not None: 
    496713        env['DIST_TARGET'] = 'i386' 
    497     elif re.search("i686", config[config_cpu]) != None: 
     714    elif re.search("i686", config[config_cpu]) is not None: 
    498715        env['DIST_TARGET'] = 'i686' 
    499     elif re.search("powerpc64", config[config_cpu]) != None: 
     716    elif re.search("powerpc64", config[config_cpu]) is not None: 
    500717        env['DIST_TARGET'] = 'powerpc64' 
    501     elif re.search("powerpc", config[config_cpu]) != None: 
     718    elif re.search("powerpc", config[config_cpu]) is not None: 
    502719        env['DIST_TARGET'] = 'powerpc' 
    503720    else: 
     
    505722    print "Detected DIST_TARGET = " + env['DIST_TARGET'] 
    506723 
    507 if ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None) or (re.search ("powerpc", config[config_cpu]) != None)): 
    508      
    509     build_host_supports_sse = 0 
    510     build_host_supports_sse2 = 0 
    511     build_host_supports_sse3 = 0 
    512  
    513     if config[config_kernel] == 'linux' : 
    514          
    515         if (env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64'): 
    516              
    517             flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1] 
    518             x86_flags = flag_line.split (": ")[1:][0].split () 
    519              
    520             if "mmx" in x86_flags: 
    521                 opt_flags.append ("-mmmx") 
    522             if "sse" in x86_flags: 
    523                 build_host_supports_sse = 1 
    524             if "sse2" in x86_flags: 
    525                 build_host_supports_sse2 = 1 
    526             #if "sse3" in x86_flags: 
    527                 #build_host_supports_sse3 = 1 
    528             if "3dnow" in x86_flags: 
    529                 opt_flags.append ("-m3dnow") 
    530              
    531             if config[config_cpu] == "i586": 
    532                 opt_flags.append ("-march=i586") 
    533             elif config[config_cpu] == "i686": 
    534                 opt_flags.append ("-march=i686") 
    535  
    536         elif (env['DIST_TARGET'] == 'powerpc') or (env['DIST_TARGET'] == 'powerpc64'): 
    537  
    538             cpu_line = os.popen ("cat /proc/cpuinfo | grep '^cpu'").read()[:-1] 
    539  
    540             ppc_type = cpu_line.split (": ")[1] 
    541             if re.search ("altivec", ppc_type) != None: 
    542                 opt_flags.append ("-maltivec") 
    543                 opt_flags.append ("-mabi=altivec") 
    544  
    545             ppc_type = ppc_type.split (", ")[0] 
    546             if re.match ("74[0145][0578]A?", ppc_type) != None: 
    547                 opt_flags.append ("-mcpu=7400") 
    548                 opt_flags.append ("-mtune=7400") 
    549             elif re.match ("750", ppc_type) != None: 
    550                 opt_flags.append ("-mcpu=750") 
    551                 opt_flags.append ("-mtune=750") 
    552             elif re.match ("PPC970", ppc_type) != None: 
    553                 opt_flags.append ("-mcpu=970") 
    554                 opt_flags.append ("-mtune=970") 
    555             elif re.match ("Cell Broadband Engine", ppc_type) != None: 
    556                 opt_flags.append ("-mcpu=cell") 
    557                 opt_flags.append ("-mtune=cell") 
    558  
    559     if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \ 
    560        and build_host_supports_sse and env['ENABLE_OPTIMIZATIONS']: 
    561         opt_flags.extend (["-msse", "-mfpmath=sse"]) 
    562         env['USE_SSE'] = 1 
    563  
    564     if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \ 
    565        and build_host_supports_sse2 and env['ENABLE_OPTIMIZATIONS']: 
    566         opt_flags.extend (["-msse2"]) 
    567         env['USE_SSE2'] = 1 
    568  
    569     #if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \ 
    570        #and build_host_supports_sse2 and env['ENABLE_OPTIMIZATIONS']: 
    571         #opt_flags.extend (["-msse3"]) 
    572         #env['USE_SSE3'] = 1 
    573  
    574     # build for 64-bit userland? 
    575     if env['DIST_TARGET'] == "powerpc64": 
    576         print "Doing a 64-bit PowerPC build" 
     724#=== Begin Revised CXXFLAGS ========================================= 
     725# comment on DIST_TARGET up top implies it can be used for cross-compiling 
     726# but that's not true because even if it is not 'auto' the original 
     727# script still reads /proc/cpuinfo to determine gcc arch flags. 
     728# This script does the same as the original. Needs to be fixed someday. 
     729cpuinfo = CpuInfo() 
     730if cpuinfo.is_x86: 
     731    opt_flags.extend(cc_flags_x86(cpuinfo, env['ENABLE_OPTIMIZATIONS'])) 
     732if cpuinfo.is_powerpc: 
     733    opt_flags.extend(cc_flags_powerpc(cpuinfo, env['ENABLE_OPTIMIZATIONS'])) 
     734if '-msse' in opt_flags: 
     735    env['USE_SSE'] = 1 
     736if '-msse2' in opt_flags: 
     737    env['USE_SSE2'] = 1 
     738 
     739m32 = is_userspace_32bit(cpuinfo) 
     740print 'User space is %s' % (m32 and '32-bit' or '64-bit') 
     741if cpuinfo.is_powerpc: 
     742    if m32: 
     743        print "Doing a 32-bit PowerPC build for %s CPU" % cpuinfo.ppc_type 
     744        machineflags = { 'CXXFLAGS' : ['-m32'] } 
     745    else: 
     746        print "Doing a 64-bit PowerPC build for %s CPU" % cpuinfo.ppc_type 
    577747        machineflags = { 'CXXFLAGS' : ['-m64'] } 
    578         env.MergeFlags( machineflags ) 
    579     elif env['DIST_TARGET'] == "x86_64": 
    580         print "Doing a 64-bit x86 build" 
     748    env.MergeFlags( machineflags ) 
     749elif cpuinfo.is_x86: 
     750    if m32: 
     751        print "Doing a 32-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name) 
     752        machineflags = { 'CXXFLAGS' : ['-m32'] } 
     753    else: 
     754        print "Doing a 64-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name) 
    581755        machineflags = { 'CXXFLAGS' : ['-m64'] } 
    582         env.MergeFlags( machineflags ) 
    583756        needs_fPIC = True 
    584     else: 
    585         print "Doing a 32-bit build" 
    586         machineflags = { 'CXXFLAGS' : ['-m32'] } 
    587         env.MergeFlags( machineflags ) 
     757    env.MergeFlags( machineflags ) 
     758#=== End Revised CXXFLAGS ========================================= 
     759 
    588760 
    589761if needs_fPIC or ( env.has_key('COMPILE_FLAGS') and '-fPIC' in env['COMPILE_FLAGS'] ):