-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
translate-c: correctly translate pointers to cv-qualified values #23394
base: master
Are you sure you want to change the base?
Conversation
8ef7887
to
77f26d5
Compare
I think using a wrapper struct for volatile types could also work: pub fn Volatile(T: type) type {
return extern struct {
inner: T,
pub inline fn load(v: *volatile Volatile(T)) T {
return v.inner;
}
};
}
const mmio_int = Volatile(c_int);
const mmio_int_ptr = *volatile c_int;
const hw_t = struct {
reg: mmio_int,
regs: [4]mmio_int,
regm: [2][2]mmio_int,
ptr: mmio_int_ptr,
};
extern var hw: *hw_t;
pub export fn entry() void {
_ = hw.reg.load();
_ = hw.regs[2].load();
} It does seems to generate correct LLVM IR and is a much better solution at least in the case of renaming the typedef to The check for volatile qualifiers should also exclude pointers since those can be volatile in Zig too. |
Agreed, that's much nicer. Does it make sense to put this in
Correct me if I'm wrong, and I may have misunderstood what you meant here, but I think Example output from zig translate-c with this PR's branch (as-is): static volatile int* foo(volatile int **x) {
return *x;
} pub fn foo(arg_x: [*c][*c]volatile c_int) callconv(.c) [*c]volatile c_int {
var x = arg_x;
_ = &x;
return x.*;
} No explicit cast here, since the pointee of |
NOTE: just realized that you may have meant that adding explicit casts when using "existing" volatile pointers isn't necessary. If this isn't what was meant then feel free to ignore I think it's safer to add casts whenever dereferencing because the pointer might not be correctly typed at the point of usage (even though the type is valid in zig). My main concern is that given the following zig expression produced by translate-c: *(&some_volatile_int) // assume some_volatile_int has type 'volatile int' was previously mistranslated to: (&some_volatile_int).* even though it is "structurally" correct and the type of The issue is that by not explicitly casting on usage, the way the pointer is obtained matters, which isn't really checked by translate-c. Now of course this PR diagnoses the above code by correctly restoring qualifiers when the address-of operator is used, but I'm personally not sure that every way to obtain a pointer to a cv-qualified value can be handled in this way. There are others situations wherein pointers can sort of just appear out of nowhere in C (like If this doesn't seem like an issue or if I'm wrong, then the explicit casts for pointer usage can be dropped. Just want to make sure we're on the same page |
Yes.
You're right, seems like I got a bit confused there. |
Instead of using
const x = hw_struct.*.load();
const x = hw_struct.*.ptr().*;
hw_struct.*.load() = 0; // doesn't work
hw_struct.*.ptr().* = 0; // ok Using I still kept the |
I don't know if this has any bearing on this PR, but I seem to recall that there are C ABIs where e.g. |
That would likely be problematic here. Do you mean that they have different sizes/alignment? If so, that's a problem, and the few temporary tests I wrote for If it's related to calling conventions (which, now that I think of it, don't most ABIs have different parameter passing conventions for structs?), that can be solved by Regardless, this made me realize that this PR doesn't handle the following simple case of assignment: typedef volatile int mmio_int; // const mmio_int = Volatile(c_int)
mmio_int x = ...; // const x: mmio_int = ...
mmio_int y = x; // ok
// will not be a volatile load (it is in C)
volatile int y = x;
// will be a type error (assign struct to int)
int y = x; I think I just need to add a check for lvalue->rvalue conversions in If there's a parameter passing convention mismatch, I also need to make sure the function parameter types are not translated to |
My memory is fuzzy here, but I believe it has to do with the calling convention, yes. I think size/alignment are the same. |
Fix #23329.
TLDR: const/volatile-qualified value-types (non-pointer types) in C are not always correctly translated when pointers to such types are taken.
For example, the following C code is mistranslated:
The penultimate line will not be a volatile load, because the type of
reg
in the translated zig code is just a bareint
. The last line will generate a regular non-volatileint
pointer for the same reason.Fixed by explicitly casting pointers to the correct qualified types whenever the qualifiers can legally appear in zig, i.e. when taking the address of a cv-qualified value and right before dereferencing pointers thereto.
Additionally, typedefs for volatile-qualified value-types are annotated by prefixing the typename with. Volatile-qualified value-types are converted to a helper typevolatile_
std.zig.c_translation.Volatile(T)
.TODO
todo
comments when associated notes/questions are resolvedAdd doc comments forrestoreValueQualifiersValue
/restoreValueQualifiersPtr
Volatile(T)
and bitcast/ptrcast when such types are passed to func params