Zig: ability to specify target CPU features

Created on 13 Jul 2019  路  16Comments  路  Source: ziglang/zig

Zig currently will take full advantage of native CPU features when compiling for the native target, however when cross compiling, there is no way to specify which CPU features to enable.

  • [ ] Add the list of possible options to zig targets so that they are discoverable
  • [ ] Add the CLI parameters to specify target CPU features
  • [ ] Make sure those options are passed to zig cc when compiling C code and don't forget to add the params to the cache hash.
  • [ ] Expose the CPU features in @import("builtin").
  • [ ] Add support to the zig build system
enhancement stage1 zig build system

All 16 comments

related #2595

I plan to continue working on my PR (#2595) shortly. With the zig cc concern, I am thinking it might be best to keep the flag names consistent with clang/LLVM (-mcpu and -mattr), although I am not a fan of the -mattr name since even LLVM's C library uses the term "features".

Can't we use the --target of clang for this? As far as I understand you can specify everything you can specify for -mcpu there. In addition the -mfloat and other options are specified with this as well. I am not sure whether the target tripple includes information about -mfpu and/or other cpu specifics?

@ibutra Can you show an example of specifying all this information with --target? My current understanding is that only architecture, OS, and C ABI are in the target triple string.

You are right I can't specify everything.
But then we should add other options as well like -mcpu, -mfpu and -mfloat-abi as is recommended by clang itself.
@layneson Can you include those options as well? Otherwise I can make a pull request just for these once yours is done (to ensure consitency).

My PR already supports mcpu. As for the others, I would, although I am unsure of how to pass those along to LLVM. The current entry point for setting cpu and features is LLVMCreateTargetMachine, which doesn't seem to take those float options directly.

Matching the CLI of clang seems reasonable, however we do need to bring these concepts into zig land. A good start will be enumerating the options in target.hpp/target.cpp and exposing a corresponding type in @import("builtin"). Also they should be printed with zig targets.

This answere on StackOverflow shows how to at least get supported cpus and attributes/features by llvm.

~I couldn't find any information about fpu yet. ~

As for the float-abi I am unsure whether this is needed as it can be specified as part of the target tripple as far as I can see. Edit: I looked into this and llvm only has the values Default,Soft and Hard so that should be covered by the target tripple.Source

And here are the fpus for ARM: Link

As far as I can see all cpus and features are defined in this folder in the .def files

I believe that when you select a cpu you essentially select a list of
features that are known to be implemented on that cpu.

Specifying specific features would then only be needed if there were
something special about your particular cpu that was different or if there
wasn't a pre-defined cpu that matched your specific hardware.

For example, from
http://www.llvm.org/svn/llvm-project/llvm/trunk/lib/Target/ARM/ARM.td

def : ProcessorModel<"cortex-m4", CortexM4Model,        [ARMv7em,
                                                         FeatureVFP4_D16_SP,
                                                         FeaturePrefLoopAlign32,
                                                         FeatureHasSlowFPVMLx,
                                                         FeatureUseMISched,
                                                         FeatureUseAA,

FeatureHasNoBranchPredictor]>;

I think that if you were to do a triple of armv7em-freestanding-eabihf,
this is a valid triple for several cpus (Cortex-M4, Cortex-M7) with
different features.

Then if you were to further do -mcpu=cortex-m4, LLVM would know to
generate code that used the specific FPU on that cpu (vfp4-sp-d16), along
with the other attributes like that there is no branch predictor. The
Cortex-M7 has a different FPU (several possible ones?).

Is it possible to modify the cpu configuration further by adding and
removing attributes?

It's been a while since I looked at this, and I'm at work so I can't double
check, but the features that make up vfp4-sp-d16 are actually 3 different
attributes in the list that is returned when using llc -march=arm -mattr=help.

@andrewrk by "enumerating the options", do you mean enumerate all features/CPUs for each target zig supports? I ask because it is a long list that would have to be manually synchronized with LLVM, although there does not appear to be a way to query the LLVM API for the features and CPUs supported by a target (the lists are private with no clear way to retrieve them), so I am thinking that enumerating them might be the play.

Yep that's what I mean. Currently the only backend of zig is LLVM but the language specification will not mention LLVM at all. LLVM is an implementation detail so the command line arguments and builtin declarations will not have LLVM-specific names.

Here's a question concerning which CPUs and features to adopt... currently, the list of supported targets for Zig is based completely on what is available in LLVM, since that is the only backend as of now. Should all CPUs/features (defined for the targets supported by Zig) be blindly copied from LLVM? If not, this raises more questions concerning how to choose what CPUs/features to include. Otherwise, since the list of CPUs and features supported by LLVM is very long, future backends may not support all features. It seems as if a mapping between backends and supported targets/CPUs/features will be necessary, but not until a system to support multiple backends is in place.

For now, I think that mirroring LLVM's CPUs/features is appropriate. Since a "CPU" in this case is just a list of features, the mappings here will be difficult to express using the enum-and-array situation currently used to describe the available targets/subtargets. I think having a Zig analog to LLVM's FeatureBitset would be helpful here, since each CPU could have its feature set defined in static memory.

I've spent a bit of time researching how LLVM features and CPUs are defined, since Zig's only backend is LLVM at the moment and thus Zig's target-specific options need to map to LLVM's. Since each target is quite different, LLVM's TableGen target description language allows arbitrary feature nesting that gets resolved at compile time.

Example: the AVR target defines Families, each of which contains the features of other Families and possibly additional features. From there, Devices are defined, each of which has an associated Family and possibly some additional features. Each Family is included in the final list of available features, as well as each "underlying" feature included in the Families. Thus, LLVM features can contain other features.

TableGen boils these hierarchies down to a list of features when it generates subtarget code. Then the frontend -mattr and -mcpu flags define the feature and cpu strings respectively, which is the unified interface for specifying options of all targets. This is why the available features for some targets are a hodge-podge of different types of values (for example, AVR's available features include architecture families and underlying features, and ARM's features include processor families, architectures, instruction sets, fpus, and features such as cryptography support).

How should Zig structure this information? LLVM has left the structure up to the targets since each one is quite different with respect to family/instruction set/subarch hierarchies. My proposal is to create abstract "catch-alls" for feature categories that should probably be separate. For instance, we could have Cpus, each of which can (optionally) include a list of base Features, Familys which contain sets of base features, and Fpus. Another option is to simplify everything by lumping all types of features together without allowing them to contain other features, and only group them within Cpuss.

A further question concerns how the initial CPU and feature data will be imported into Zig. Blindly copying LLVM will be difficult due to the arbitrary target representation mentioned above. I propose that important, well-known features be added for major targets initially, with others added as requested. This way, the new target abstractions can be tested on a minimal subset and the issues referenced above can be addressed ASAP.

Thoughts? (@andrewrk)

@layneson Your approach sounds reasonable to me.

I propose that important, well-known features be added for major targets initially, with others added as requested. This way, the new target abstractions can be tested on a minimal subset and the issues referenced above can be addressed ASAP.

:+1:

For instance, we could have Cpus, each of which can (optionally) include a list of base Features, Familys which contain sets of base features, and Fpus. Another option is to simplify everything by lumping all types of features together without allowing them to contain other features, and only group them within Cpuss.

Either of these seems fine to me.

One note: I think we can probably do all of this work self-hosted (in .zig code) and call into it from stage1, like we already do for lots of stuff.

I believe the zig code would only have to expose to stage1 a function that essentially translated "zig format" CLI parameter data into "LLVM format". All the data structures, parsing, and understanding of what these features mean, etc, could be in zig code.

Landed in 96e5f476c3f7f44d0b299bc6043f3fd88769bd8b

Now that's fixed and #3033 has been merged, can you unskip this test?

Was this page helpful?
0 / 5 - 0 ratings