-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Implement strncmp #778
Implement strncmp #778
Conversation
tests/codegen.cpp
Outdated
@@ -77,6 +77,7 @@ | |||
#include "codegen/pred_binop.cpp" | |||
#include "codegen/string_equal_comparison.cpp" | |||
#include "codegen/string_not_equal_comparison.cpp" | |||
#include "codegen/strncmp.cpp" |
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.
This is not needed anymore :)
Overall code lgtm. Will do some more testing later.
LLVM is pretty strict with Int types, it won't let you use most binary operators between two integers of different size. diff --git a/src/ast/irbuilderbpf.cpp b/src/ast/irbuilderbpf.cpp
index c8aa040..9778aed 100644
--- a/src/ast/irbuilderbpf.cpp
+++ b/src/ast/irbuilderbpf.cpp
@@ -378,7 +378,7 @@ Value *IRBuilderBPF::CreateStrcmp(Value* val, std::string str, bool inverse) {
Value *IRBuilderBPF::CreateStrncmp(Value* val, std::string str, uint64_t n, bool inverse) {
Function *parent = GetInsertBlock()->getParent();
BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent);
- AllocaInst *store = CreateAllocaBPF(getInt8Ty(), "strcmp.result");
+ AllocaInst *store = CreateAllocaBPF(getInt64Ty(), "strcmp.result");
CreateStore(getInt1(inverse), store);
@@ -436,7 +436,7 @@ Value *IRBuilderBPF::CreateStrncmp(Value* val1, Value* val2, uint64_t n, bool in
*/
Function *parent = GetInsertBlock()->getParent();
BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent);
- AllocaInst *store = CreateAllocaBPF(getInt8Ty(), "strcmp.result");
+ AllocaInst *store = CreateAllocaBPF(getInt64Ty(), "strcmp.result");
BasicBlock *done = BasicBlock::Create(module_.getContext(), "strcmp.done", parent);
CreateStore(getInt1(inverse), store); |
8dc5bc5
to
bb9d422
Compare
Thanks for the pointers, that was very helpful! While trying to fix this test:
I discovered that
I'll ignore that for now because that seems like a seperate issue though. I'm still not sure why the LLVM 5 debug build is failling (it seems to be another similar issue), but otherwise I think this should be relatively good. |
Any chance I could get another round of review? The only other issue I am aware of is the LLVM 5 debug build failing. I'm not sure exactly why, as far as I know this error is in actually running the code, and not in the IR verification. Unfortunately getting LLVM5 is a little tough for me at the moment, but if the answer isn't obvious I can try to find a VM to debug further. Thanks in advance! :)
|
%strcmp.cmp16 = icmp eq i8 %12, 0 | ||
br i1 %strcmp.cmp16, label %pred_true, label %pred_false | ||
|
||
lookup_success: ; preds = %pred_true |
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.
Why is this whole section dissapearing? That can't be ok
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.
vagrant@ubuntu-cosmic:~/bpftrace$ sudo bpftrace_master -e 't:syscalls:sys_enter_openat /comm == "cat"/ { @[comm]++ }' -c "cat xxx"
Attaching 1 probe...
/bin/cat: xxx: No such file or directory
@[cat]: 25
vagrant@ubuntu-cosmic:~/bpftrace$ sudo bpftrace -e 't:syscalls:sys_enter_openat /comm == "cat"/ { @[comm]++ }' -c "cat xxx"
Attaching 1 probe...
/bin/cat: xxx: No such file or directory
WIth your patch the whole function body seems to be optimized away. The optimized IR doesn't contain the map lookup and store code
src/ast/irbuilderbpf.cpp
Outdated
@@ -427,14 +436,14 @@ Value *IRBuilderBPF::CreateStrcmp(Value* val1, Value* val2, bool inverse) { | |||
*/ | |||
Function *parent = GetInsertBlock()->getParent(); | |||
BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent); | |||
AllocaInst *store = CreateAllocaBPF(getInt8Ty(), "strcmp.result"); | |||
AllocaInst *store = CreateAllocaBPF(getInt64Ty(), "strcmp.result"); |
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.
why this change?
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.
This was added due to this comment, essentially doing a simple compare in an if statement was failing because of a type mismatch. I'm not sure why this is causing the map lookup code to be removed (reverting this does fix that), I'll try to figure out why that's happening...
0c102f1
to
468d260
Compare
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.
Almost there :) thanks for working on this. Sorry for the lag.
Can you add some documentation too?
src/ast/semantic_analyser.cpp
Outdated
@@ -559,6 +559,17 @@ void SemanticAnalyser::visit(Call &call) | |||
else if (call.func == "ustack") { | |||
check_stack_call(call, Type::ustack); | |||
} | |||
else if (call.func == "strncmp") { | |||
check_nargs(call, 3); |
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.
To prevent going out of range:
if(check_nargs(call, 3)) {
check_arg(call, Type::string, 0);
check_arg(call, Type::string, 1);
check_arg(call, Type::integer, 2, true);
Integer &size = static_cast<Integer&>(*call.vargs->at(2));
if (size.n < 0)
err_ << "Builtin strncmp requires a non-negative size" << std::endl;
}
call.type = SizedType(Type::integer, 8);
}
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.
bpftrace -e 'i:s:1 { strncmp("a"); }'
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 1) >= this->size() (which is 1)
Aborted
src/ast/semantic_analyser.cpp
Outdated
else if (call.func == "strncmp") { | ||
check_nargs(call, 3); | ||
check_arg(call, Type::string, 0); | ||
check_arg(call, Type::string, 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.
Can you add a few semantic analyser tests for this? Especially the "wrong" cases are good to test, e.g.:
TEST(semantic_analyser, strncmp)
{
test("i:s:1 { strncmp(1) }", 1);
test("i:s:1 { strncmp(1,1,1) }", 1);
test("i:s:1 { strncmp("a",1,1) }", 1);
test("i:s:1 { strncmp("a","a",-1) }", 1);
}
b98ce06
to
1a8bdf7
Compare
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.
Just selecting "request changes" so this doesn't get merged while this is discussed:
Shouldn't strncmp
return 0 on a string match instead? That behaviour would be in line with the standard C version of this function.
Sure, that seems fine to me, it's a little less convenient but it is better to follow the standard strncmp return values. I don't think I can emulate the greater than/less than zero behavior without a little more work (which I'd rather leave for a separate diff), but zero/non-zero should be easy. |
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.
Looks good to me, just one minor change in the semantic analyser needed
src/ast/semantic_analyser.cpp
Outdated
check_arg(call, Type::string, 1); | ||
check_arg(call, Type::integer, 2, true); | ||
|
||
Integer &size = static_cast<Integer&>(*call.vargs->at(2)); |
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.
At this point it's not guaranteed that the third argument is an integer literal, so this could lead to some dodgy memory accesses. This section needs to only run if check_arg(call, Type::integer, 2, true)
returns true
While this is slightly less convenient for quick scripting, it's in-line with strncmp in the stdlib.
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.
Nice - thank you!
I'll be honest and say I mostly have no idea what I'm doing here, so I'd appreciate it if I could get some extra review and guidance for this. This is my first time working with any serious compiler stuff, and my first real exposure to LLVM. I know you're probably busy for the 0.9.1 release, so feel free to put this on the backburner until you have more time.
For some reason:
results in
but,
if(1)
orif(strncmp("hhvm", str($1), -1) != 0)
work fine. I think I'm missing something in the semantic analyzer but I'm not sure. It might need some changes to theif
logic, but I'm not sure what/where those would be.Fixes #459