I'm working on a procedural macro that does a lot of work that can slow down compilation considerably. The work done does not effect the semantics of the function; that is, if given the same set of arguments, the returned value is does not change depending on whether the macro is applied.
In an effort to make the edit-comp-test loop quicker, I would like to make the macro a no-op depending on conditions that relate to how the crate is being compiled. I would like to be able to determine two properties in particular:
Why the macro is being executed: Build/Run, Documentation, Testing
Whether the macro being executed for optimized builds.
Cargo exposes the optimization level to build scripts (through environment variables OPT_LEVEL and PROFILE) but does not expose the mode (Build, Documentation, ..). However, none of this information seems to be exposed to procedural macros at all.
Related
I'm a little confused going about adding a new instruction to QEMU and want to confirm if my understanding is right. After going through the source code, I think adding an instruction to QEMU involves the following steps:
Define a helper function of the format CHERI_HELPER_IMPL(*instruction* in \target\target_arch\op_helper.c that emulates this instruction.
Define generate_*instruction* in \target\target_arch\translate.c that calls gen_helper_*instruction* which calls the helper function.
Am I missing any steps?
The fact that you mention a "CHERI_HELPER_IMPL" macro tells me that you're not working with upstream QEMU, but with the CHERI project's fork of it. So you should talk to them about anything special that might be needed there. As I understand it their local modifications may be quite significant.
For upstream QEMU, this depends on whether the target architecture is using decodetree or not.
For decodetree-based architectures:
add a suitable instruction pattern or patterns to the .decode file. This will result in the generation of code which calls a function whose name begins trans_ to handle instructions that match that pattern, passing it a pointer to a structure which contains the values of the various instruction fields defined by your pattern.
implement the trans_ functions appropriately. What you need to do depends on what the instruction behaviour is. For simple instructions, you can just emit TCG ops which do the actions the instruction must do. For more complicated work, you might want to emit TCG ops for "call a runtime helper function". The tcg/README file has some "recommended coding rules" at the bottom which include a rule of thumb for when to use a helper function.
if you decided to emit a helper call, you need to implement the helper function. The DEF_HELPER_* macros in helper.h both define the prototype for the C function you're going to write and also auto-generate a function gen_helper_whatever that your translate-time code can call to generate the TCG code to call it.
For non-decodetree-based architectures:
There will be hand-written code, usually starting in translate.c, which identifies instructions using switch statements and bit-masking code. You'll need to look at that code to find out where in that to add the code which identifies the instruction that you're adding. This is all completely target-specific; some targets use a somewhat table-driven setup or some preprocessor macros as part of this, some use completely hand-written code.
Once you've figured out where to add the "is this my instruction type?" check, the rest is similar to decodetree-based targets: you need to emit TCG ops to either do the work or to call a helper to do the work at runtime.
You'll find that there's a lot of specific detail that needs to be got right in each of these steps, but that's the basic outline.
The ctor crate doesn't support web assembly currently, although there is active discussion about how to fix this.
Although I am well aware of the issues associated with static initialization coming from C++, being able to register things with a factory at startup is a very handy capability, and is necessary to avoid violating the DRY principle in a lot of cases. Without it every time you want to add a new possibility to your factory you have to add a separate line of code to main(), possibly more lines if you need to import your new function. This quickly gets tedious.
I am wondering if it is possible to build something equivalent (at least when everything you want to register is concentrated inside a single crate) using a procedural attribute macro and build.rs. The macro would be used to mark the functions that I want to register, and it's implementation would save the module paths ("crate::your::registered_function") of those functions off to the side somewhere in a file but otherwise just be a passthrough. Then build.rs would generate a function that calls all the functions listed in the file, and I would have main() call that one function by hand.
Is there a different trick that would work instead?
Does an implementation of this already exist somewhere I could use as a reference?
How would the procedural macro actually generate the module path for the function to be called? There is module_path! but if invoked from inside the definition of the procedural macro it will give the module path for the macro, not the module path associated with what the TokenStream is going to expand into. The macro could generate a call to module_path! but that won't evaluate until later when the final program is run.
Let's say I want to use a specific Linux / POSIX feature, that is provided conditionally based on feature test macros. For example, the type cpu_set_t, the macro CPU_SET_ZERO, and the function sched_setaffinity.
Ideally I would just like to tell CMake that I need those, and it should figure out what extra feature test macros to set or fail with a nice error message if it can't be provided on the current system. Is that possible?
I am aware that I can lookup in the manpages and manually use add_definitions(-D_GNU_SOURCE), but that can become tedious once multiple functionalities that were introduced and deprecated in different versions of the POSIX standard are combined. In my experience, it can become difficult to maintain portability across different versions of the glibc implementation.
There are the CMake platform checks, but they only seem to help in checking. So I get the error during cmake rather than make, but I still have to figure out the right feature test macros manually.
cmake-compile-features seem to offer only features directly related to the compiler, not the library.
If you are just looking to determine whether a function or variable exists, you can use the CheckSymbolExists module. Likewise, there is CheckStructHasMember for structs (assuming there is a standard member you can check for). So:
include (CheckSymbolExists)
include (CheckStructHasMember)
CHECK_SYMBOL_EXISTS(CPU_SET_ZERO sched.h CPU_SET_ZERO_exists)
CHECK_SYMBOL_EXISTS(sched_setaffinity sched.h sched_setaffinity_exists)
CHECK_STRUCT_HAS_MEMBER(cpu_set_t <member?> sched.h cpu_set_t_exists)
It appears that cpu_set_t is an opaque type, so instead you can use the CheckCXXSourceCompiles module, which is a frontend for the try_compile command. It is a generic way to determine if any particular code compiles. try_compile is used extensively by 'base' CMake to determine features (try a search in the Modules directory!). Essentially, you pass it in a minimal source file, which should fail compilation if your feature is not present, and it reports back to your CMake script the result.
I am unable to identify a compiler flag that would turn off all of the seemingly pointless (with production code) calls that are presumably mainly for tracing.
--no-traces
does not accomplish this.
Calls like:
HX_STACK_LINE
HX_STACK_PUSH
Perhaps these should be able to be turned off and the APIs that rely on them disabled if necessary for production code.
I was worried about this at first as well. However, it turns out that as long as you don't define certain variables, all of those inserted lines are removed when the C++ code is compiled.
(For reference, the variables are HXCPP_DEBUGGER, HXCPP_DEBUG, HXCPP_STACK_VARS, HXCPP_STACK_LINE, and HXCPP_STACK_TRACE, and none of them are defined by default)
When the same piece of c++ code is compiled with the same version of visual c++ compiler but at different times and possibly in different computers, does the code reordering performed by the compiler remains same or it may differ. i.e. does the logic behind code optimization by code reordering depend only on the code or it depends on various other parameters?
The context of the question is that I want to create a tool which finds out whether the two dlls are same or different based on their functionalities.
Correct me if I'm wrong in assuming that since you want to compare dlls based on their functionality, you don't care about implementation details. Based on this assumption, it is clear that your tool could only look at the function signatures and classes, structs, etc definitions exposed by the dlls which would always be the same regardless of compiler for the same dll.