-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Add bswap built-in function #2166
Conversation
abb2006
to
b7df4b1
Compare
src/ast/passes/codegen_llvm.cpp
Outdated
// correct bswap intrinsic would be selected, (bswap.i16), yet LLVM would | ||
// give to it a zero extended i64 argument! The trick below seems to fix | ||
// this behaviour and the argument to bswap is always of the correct type. | ||
Value *promoted_value = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this happen in the original IR or the optimized one? We have some weird casts in there for 'reasons' which can make the optimized ir confusing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a build without the promoting / demoting trick and compared the unoptimized / optimized IRs from tcpconnect.bt
.
Unoptimized IR
%"$dport" = alloca i16, align 2
...
%75 = load i16, i16* %"struct sock_common.skc_dport", align 2
%76 = zext i16 %75 to i64
store i64 %76, i16* %"$dport", align 8
%78 = load i16, i16* %"$dport", align 2
%79 = call i16 @llvm.bswap.i16(i16 %78)
Optimized IR
%16 = load i16, i16* %"struct sock_common.skc_dport", align 2
%17 = zext i16 %16 to i64
%18 = call i16 @llvm.bswap.i16(i64 %17)
In the unoptimized version the bswap intrinsic is correctly called with a i16 argument, but the storing of an i64 value into the i16 $dport looks strange...
auto scoped_del_arg = accept(arg); | ||
|
||
assert(arg->type.IsIntegerTy()); | ||
if (arg->type.GetSize() > 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this and the assert? Isn't this guaranteed by the semantic analyser?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the assert
in order to explicitly declare this expectation / guarantee from the semantic analyser, as well as a safeguard in case the behaviour of the semantic analyzer changes in the future.
Regarding the arg->type.GetSize()
check, the intention is that bswap
is invoked only for multibyte integers, while i8 ones are simply passed through.
Should I amend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah skipping the byte case makes sense.
re assert, we should try to avoid them as the end user experience of asserts is quite bad. But in this case have test coverage for it (test("kprobe:f { bswap(\"hello\"); }", 10);
) so we will probably catch it before it ends up in the master branch
I did a more throrough investigation and I added a changeset which fixes this strange behaviour:
After running the runtime tests, some errors were reported for some test cases due to differences in the generated IRs. I had a look into the diffs and they appear to be benign. I also added a fix in the semantic analysis of bswap, because assertion failures were occurring when the tests were running in debug builds. |
Before we merge this we should run all the tools we have and make sure the output makes sense. We had some issues in the past and these weird casts are part of it, e.g. #1173 |
Went through all the programs in the Exceptions (that by all accounts are irrelevant to the changes introduced with this PR):
|
I can confirm that these should be unrelated to this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
I think that getting rid of that unconditional cast is reasonable. Looking at the changes in codegen tests, it seems that each operation does typecasts on its inputs when needed (which is the way it should be done anyway), so hopefully nothing will break.
Could you update the codegen tests so that CI passes? There's a script in scripts/
for that.
Thanks @viktormalik! I have updated the codegen tests as you proposed above. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks! Could you squash the commits, please?
The function takes an unsigned integer and returns an unsigned integer of the same width, but with the byte order reversed. Changed behaviour when reading elements from compound data structures: Previously, the returned value would unconditionally be cast to a 64 bit integer --- this would cause LLVM intrinsic invocations to fail if narrower integers were involved. Now, an integer of the same width is returned as the accessed field: it is the callers responsibility to cast to a wider integer (something which is nevertheless currently done)
@viktormalik Just pushed everything as a single comit. Thanks! |
The function takes an unsigned integer and returns an unsigned
integer of the same width, but with the byte order reversed.
Resolves #1875
One thing I'd like to draw your attention is the integer type promotion / demotion in order to fix a strange behaviour when 16 bit arguments were received. Since I am not really competent in LLVM, it is possible that something is escaping me....
Also, I have updated TCP related programs (e.g.
tcpconnect.bt
) to use the new bswap function, instead of the manual byte reordering.Checklist
man/adoc/bpftrace.adoc
and if needed indocs/reference_guide.md
CHANGELOG.md