Skip to content
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

passing rust records by value to native functions broken #1402

Closed
erickt opened this issue Dec 31, 2011 · 8 comments
Closed

passing rust records by value to native functions broken #1402

erickt opened this issue Dec 31, 2011 · 8 comments
Labels
A-codegen Area: Code generation E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.

Comments

@erickt
Copy link
Contributor

erickt commented Dec 31, 2011

I thought rust records were supposed to be compatible with c structs, but it seems there is an issue passing them by value to a native module. You can find a example of the bug here:

https://gist.github.com/1543358

@nikomatsakis
Copy link
Contributor

The c type unsigned is not the same as the Rust type uint. Try ctypes::unsigned instead.

@erickt
Copy link
Contributor Author

erickt commented Dec 31, 2011

Thanks Niko. I updated my test, but I'm still running into the same issue.

On a side note, I thought commit 7fd62bb unified uint with c unsigned integer. Am I misreading the commit?

@nikomatsakis
Copy link
Contributor

I believe you are misreading the commit. I think what that commit says is that "if you are targeting a 64-bit CPU, then uint and u64 are considered the same by the type checker". But unsigned in C is always u32 in Rust terms, at least on the architectures we target.

@erickt
Copy link
Contributor Author

erickt commented Dec 31, 2011

I found the problem. It looks like rust is passing the record by reference instead of by value. I updated my test to use fn print_foo(+f: foo) but that doesn't seem to have any effect. print_foo is still getting declared as declare i1 @print_foo({ i8*, i32 }*) in the bitcode.

@nikomatsakis
Copy link
Contributor

  • is "by copy". Try ++.

@erickt
Copy link
Contributor Author

erickt commented Dec 31, 2011

Great, that worked! The tutorial said + was by value. I'll submit a pull request to add ++ to the doc.

@erickt erickt closed this as completed Dec 31, 2011
@erickt erickt reopened this Jan 3, 2012
@erickt
Copy link
Contributor Author

erickt commented Jan 3, 2012

I'm reopening this because while passing by-value worked for one pointer value, it's breaking for two. I've updated my gist for a demonstration of the issue.

@graydon
Copy link
Contributor

graydon commented Jan 6, 2012

This is a whole lot more broken than it looks at first. The reason is subtle.

In any calling convention, there's a division of labour between frontend and backend. For clang+LLVM, for example, some decisions are made by the code making LLVM-API calls (which things to pass by pointer or by value) and some decisions are made by the code inside LLVM.

It turns out that the place we draw the line is not the same place clang+LLVM draw the line. Which means that when we break clang off and put rustc in its place, we're assuming LLVM is going to be doing a bunch of things magically that it will not. It was expecting clang to make some of those decisions itself. We're not emulating what clang does, so we are operating LLVM in a not-expected way. The result is we're asking LLVM to generate non-system-ABI-conformant calls.

The abi is here: http://www.x86-64.org/documentation/abi.pdf and if you scan through to section 3.2.3 on page 15, you'll see there are quite a number of very subtle rules. These combine nontrivially. In particular, in the section on aggregate handling, section 5 (c) is causing your structure (when it's larger than 2 "eightbytes") to spill to memory; clang generates code like this to receive one by return-value, and pass it on by value:

%agg.tmp = alloca %struct.foo, align 8
call void @make_foo(%struct.foo* sret %agg.tmp)
call void @take_foo(%struct.foo* byval align 8 %agg.tmp)

even though it could easily go in registers. It's not of class SSE. This is not something LLVM does for us. The logic for it is in clang, over here: http://llvm.org/svn/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp if you search for class X86_64ABIInfo

That code in turn appears to be driven by abstract parameter-attributes that are constructed over here http://llvm.org/svn/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp, if you search for ConstructAttributeList. I believe we are, more or less, going to have to emulate all this logic ourselves if we want to operate LLVM in such a way that it's ABI compatible.

This sucks and will be a whole lot of work. It's not going to be fixed in time for this release.

Thanks to echristo for helping diagnose this.

brson pushed a commit that referenced this issue Apr 6, 2012
lots of changes, here.. should've commited sooner.
- added uv::direct module that contains rust fns that map, neatly, to
the libuv c library as much as possible. they operate on ptrs to libuv
structs mapped in rust, as much as possible (there are some notable
exceptions). these uv::direct fns should only take inputs from rust and,
as neccesary, translate them into C-friendly types and then pass to the
C functions. We want to them to return ints, as the libuv functions do,
so we can start tracking status.
- the notable exceptions for structs above is due to ref gh-1402, which
prevents us from passing structs, by value, across the Rust<->C barrier
(they turn to garbage, pretty much). So in the cases where we get back
by-val structs from C (uv_buf_init(), uv_ip4_addr(), uv_err_t in callbacks)
, we're going to use *ctypes::void (or just errnum ints for uv_err_t) until
gh-1402 is resolved.
- using crust functions, in these uv::direct fns, for callbacks from libuv,
will eschew uv_err_t, if possible, in favor a struct int.. if at all
possible (probably isn't.. hm.. i know libuv wants to eventually move to
replace uv_err_t with an int, as well.. so hm).
- started flushing out a big, gnarly test case to exercise the tcp request
side of the uv::direct functions. I'm at the point where, after the
connection is established, we write to the stream... when the writing is
done, we will read from it, then tear the whole thing down.

overall, it turns out that doing "close to the metal" interaction with
c libraries is painful (and more chatty) when orchestrated from rust. My
understanding is that not much, at all, is written in this fashion in the
existant core/std codebase.. malloc'ing in C has been preferred, from what
I've gathered. So we're treading new ground, here!
flip1995 pushed a commit to flip1995/rust that referenced this issue Oct 9, 2020
Downgrade string_lit_as_bytes to nursery

Between rust-lang#1402 (regarding `to_owned`) and rust-lang#4494 (regarding `impl Read`), as well as other confusion I've seen hit in my work codebase involving string_lit_as_bytes (`"...".as_bytes().into()`), I don't think this lint is at a quality to be enabled by default.

I would consider re-enabling this lint after it is updated to understand when the surrounding type information is sufficient to unsize `b"..."` to &\[u8\] without causing a type error.

As currently implemented, this lint is pushing people to write `&b"_"[..]` which is not an improvement over `"_".as_bytes()` as far as I am concerned.

---

changelog: Remove string_lit_as_bytes from default set of enabled lints
coastalwhite pushed a commit to coastalwhite/rust that referenced this issue Aug 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.
Projects
None yet
Development

No branches or pull requests

3 participants