The following bitwise functions are currently supported: and(), or(), xor() and invert().
But there is no function or operator for bitwise left/right shift.
Add the lshift() and rshift() functions for bitwise left shift and right shift.
https://github.com/vim/vim/pull/8457
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
Merging #8457 (815f53a) into master (6582e23) will decrease coverage by
87.21%.
The diff coverage is0.00%.
❗ Current head 815f53a differs from pull request most recent head da21dcf. Consider uploading reports for the commit da21dcf to get more accurate results
@@ Coverage Diff @@ ## master #8457 +/- ## =========================================== - Coverage 89.71% 2.50% -87.22% =========================================== Files 149 147 -2 Lines 167841 162656 -5185 =========================================== - Hits 150584 4068 -146516 - Misses 17257 158588 +141331
| Flag | Coverage Δ | |
|---|---|---|
| huge-clang-none | ? |
|
| huge-gcc-none | ? |
|
| huge-gcc-unittests | 2.50% <0.00%> (-0.01%) |
⬇️ |
Flags with carried forward coverage won't be shown. Click here to find out more.
| Impacted Files | Coverage Δ | |
|---|---|---|
| src/evalfunc.c | 0.00% <0.00%> (-95.84%) |
⬇️ |
| src/float.c | 0.00% <0.00%> (-98.91%) |
⬇️ |
| src/digraph.c | 0.00% <0.00%> (-97.78%) |
⬇️ |
| src/gui_gtk_f.c | 0.00% <0.00%> (-97.54%) |
⬇️ |
| src/match.c | 0.00% <0.00%> (-97.13%) |
⬇️ |
| src/crypt_zip.c | 0.00% <0.00%> (-97.06%) |
⬇️ |
| src/sha256.c | 0.00% <0.00%> (-96.94%) |
⬇️ |
| src/evalbuffer.c | 0.00% <0.00%> (-96.83%) |
⬇️ |
| src/cmdhist.c | 0.00% <0.00%> (-96.63%) |
⬇️ |
| src/debugger.c | 0.00% <0.00%> (-96.62%) |
⬇️ |
| ... and 136 more |
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing data
Powered by Codecov. Last update 6582e23...da21dcf. Read the comment docs.
@dpelle requested changes on this pull request.
> @@ -7175,6 +7177,15 @@ log10({expr}) *log10()*
<
{only available when compiled with the |+float| feature}
+lshift({expr1}, {expr2}) *lshift()*
+ Bitwise left shift {expr1} by {expr2}. The arguments must be
+ Numbers. Other types of arguments cause an error.
Usually bit shifts in C are done with unsigned (although it's also possible with signed).
Vim script does not have unsigned.
We should document how it behaves with the sign bit. In other words, what is the result of lshift(-2, 1)?
And we should test with negative numbers too.
In Java, left-shift has 2 different operators >> (sign extension) and >>> (shifts a zero into the leftmost position). See: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html So maybe we need an extra (optional?) parameter to indicate whether to extend sign or not?
In src/evalfunc.c:
> @@ -6189,6 +6195,21 @@ f_line2byte(typval_T *argvars UNUSED, typval_T *rettv)
#endif
}
+/*
+ * "lshift()" function
+ */
+ static void
+f_lshift(typval_T *argvars, typval_T *rettv)
+{
+ if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_NUMBER)
+ {
+ emsg(_(e_invarg));
+ return;
+ }
+ rettv->vval.v_number = tv_get_number(&argvars[0])
+ << tv_get_number(&argvars[1]);
This will result in UB (undefined behavior) if we do e.g. lshift(1, 100) as in C, it is UB to bit-shifts by more bits than the number of bits in the number.
In Vim script, we don't want such undefined behavior. I'd say lshift(1, 100) should return 0, and we must thus return 0 if argvars[1] is too high. And we can add a test for bit shift with large number of bits.
We can verify that there is no runtime error using ubsan.
Also, what should happen when shifting by a negative number of bits? E.g. lshift(1, -1) In C, it is undefined behavior but again, we don't want UB in Vim script.
Yegappen wrote:
> The following bitwise functions are currently supported: and(), or(),
> xor() and invert().
> But there is no function or operator for bitwise left/right shift.
> Add the lshift() and rshift() functions for bitwise left shift and
> right shift.
I'm wondering if it is worth it. You can also use "* 2" for left shift
and "/ 2" for right shift. It's not exactly the same (considering
overflow), but does that matter in a Vim script?
@yegappan commented on this pull request.
> @@ -7175,6 +7177,15 @@ log10({expr}) *log10()*
<
{only available when compiled with the |+float| feature}
+lshift({expr1}, {expr2}) *lshift()*
+ Bitwise left shift {expr1} by {expr2}. The arguments must be
+ Numbers. Other types of arguments cause an error.
Thanks for the review. Yes. We can add an option to rshift() to deal with sign extension.
In src/evalfunc.c:
> @@ -6189,6 +6195,21 @@ f_line2byte(typval_T *argvars UNUSED, typval_T *rettv)
#endif
}
+/*
+ * "lshift()" function
+ */
+ static void
+f_lshift(typval_T *argvars, typval_T *rettv)
+{
+ if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_NUMBER)
+ {
+ emsg(_(e_invarg));
+ return;
+ }
+ rettv->vval.v_number = tv_get_number(&argvars[0])
+ << tv_get_number(&argvars[1]);
I will add a check for argvars[1] and return 0 if it is too large. If the shift value is negative, we can return the original value.
—
You are receiving this because you commented.
I appear to have missed this one. Bit shift does not apply to floats, your example does not seem representative.
lshift(val, 4) would be equivalent to "val * 16" and rshift(val, 4) equivalent to "val / 16".
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub.
Triage notifications on the go with GitHub Mobile for iOS or Android.
![]()
I appear to have missed this one. Bit shift does not apply to floats, your example does not seem representative.
lshift(val, 4) would be equivalent to "val * 16" and rshift(val, 4) equivalent to "val / 16".
—
You are receiving this because you are subscribed to this thread.
Any update on this PR? The pow+float2nr approach works but is quite a mouthful, having the shift operators would greatly complement the other bit ops.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I have no see a response to my suggestion to use "N * 16" to shift 4 bits left and "N / 16" to shift 4 bits right.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I mean, why it would be needed to specify the number of bits. When do you not know that beforehand?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
Sometimes I use bitsets to keep track of a small (< bit size of number variable) number of boolean elements, that's much faster than eg. having a list with N bool elements.
Testing whether the i-th element is present can be achieved with set & (1 << index) and unless you know all the indices beforehand you're bound to use a runtime-known shift value.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I was trying to find another language that uses a function for bitwise shift, but it appears all the popular languages use ">>" and "<<" operators. Would that be possible in Vim as well, or would it create a conflict?
It might seem a bit inconsistent with the other bitwise functions though.
Another thing to consider is to use one function, where a negative argument is used to shift in the other direction. Again, this would depend on how other languages do this, what people are used to.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
it appears all the popular languages use ">>" and "<<" operators
Java uses >>> and >> which has different behavior for the bit sign.
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Something to consider if Vim script has bit shift operators.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@LemonBoy commented on this pull request.
In src/vim9.h:
> @@ -151,6 +151,9 @@ typedef enum {
ISN_COMPAREFUNC,
ISN_COMPAREANY,
+ // bitwise left/right shift operation
This should be another case of ISN_OPNR instead of having its own opcode.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@LemonBoy commented on this pull request.
> + if (tv2->vval.v_number > MAX_LSHIFT_BITS)
+ {
+ SOURCING_LNUM = iptr->isn_lnum;
+ emsg(_(e_bitshift_value_too_large));
+ goto on_error;
+ }
+
+ if (exprtype == EXPR_LSHIFT)
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number << tv2->vval.v_number;
+ else
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number >> tv2->vval.v_number;
+
+ // clear the topmost sign bit
+ tv1->vval.v_number &=
I think this to be quite weird, I'd let the user deal with negative numbers rather than making the integers N-1bit wide.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@yegappan commented on this pull request.
In src/vim9.h:
> @@ -151,6 +151,9 @@ typedef enum {
ISN_COMPAREFUNC,
ISN_COMPAREANY,
+ // bitwise left/right shift operation
I have updated the PR to use ISN_OPNR instead of using a new opcode.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@yegappan commented on this pull request.
> + if (tv2->vval.v_number > MAX_LSHIFT_BITS)
+ {
+ SOURCING_LNUM = iptr->isn_lnum;
+ emsg(_(e_bitshift_value_too_large));
+ goto on_error;
+ }
+
+ if (exprtype == EXPR_LSHIFT)
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number << tv2->vval.v_number;
+ else
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number >> tv2->vval.v_number;
+
+ // clear the topmost sign bit
+ tv1->vval.v_number &=
This is based on the following comment from Bram:
Since bitwise operators should not care about a sign bit, I would very
much prefer ">>" to just make the topmost bit zero. No need for ">>>"
then.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
" The topmost bit (sign bit) is always cleared " that is for the ">>" operator, not for "<<".
For operator precedence it looks like we need a new level. It's in between "+" and "-"
and the comparative operators. Thus between eval4() and eval5().
This is what Ecmascript specifies:
https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-left-shift-operator
Are there languages that use a different precedence? I know have have often been confused by this, but we should stick to what most other popular languages do.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@errael commented on this pull request.
> + if (iptr->isn_arg.op.op_type == EXPR_RSHIFT) + // clear the topmost sign bit + res &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);
This fixup only works if arg2 is 1. Something like following is needed.
result &= (1 << arg2) - 1 << NBITS - arg2
If right shifted by 3 and NBITS is 8 then this should be &= 1110_0000.
If you can count on the compiler to respect unsigned and make sure zeros are shifted in, then you don't need a fixup. If you can't count on the compiler, this might be more understandable and doesn't need a fixup.
res = arg1 >> 1;
res &= ~(1 << MAX_LSHIFT_BITS);
res = res >> arg2 - 1;
None of this is tested, simulated on paper... (I looked up the precedence against +/-)
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@errael commented on this pull request.
> + if (iptr->isn_arg.op.op_type == EXPR_RSHIFT) + // clear the topmost sign bit + res &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);
Oops, updated to add the missing ~ and &= 0001_1111
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
This is based on the following comment from Bram:
Ah I see, the comment didn't get posted to GH for some reason. That's fine as long as it's clearly stated in the docs that the operators perform logical shifts (opposed to arithmetic ones).
The sign-clearing is useless as you're already performing the shift on unsigned values.
The next logical step is to extend the and/or/not operators to work with numbers, right now shifts are the only exception among the bitwise ops.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
In a performance sensitive function I recently used vim's and(arg1, arg2) and or(arg1, arg2) since I couldn't find &,|. Got me wondering what the difference is between a function and vim9 instruction execution; so I was interested in taking a look at how this instruction executes.
I guess if invoking and() involves setting up a stack frame then the difference is pretty big.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
the and/or/not operators to work with numbers
xor gets no respect.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@DominiquePelle-TomTom commented on this pull request.
> @@ -946,4 +946,48 @@ func Test_string_interp() call v9.CheckDefAndScriptSuccess(lines) endfunc +" Test for bitwise left and right shift (<< and >>) +func Test_bitwise_shift() + let lines =<< trim END + call assert_equal(16, 1 << 4) + call assert_equal(2, 16 >> 3) + call assert_equal(0, 0 << 2) + call assert_equal(0, 0 >> 4) + call assert_equal(3, 3 << 0) + call assert_equal(3, 3 >> 0) + call assert_equal(0, 0 >> 4)
What happens if you do:
42 >> -1 (in C, shifting by negative number is undefined behavior, in Vim it should be an error or maybe 0?)42 << 100 or 42 >> 100 (in C, shift by more bits than the underlying type is undefined behavior, in Vim it should be an error or maybe 0?)—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I don't spot a test case for repeating the operator, e.g. 1 << 2 << 3
I believe this should work like (1 << 2) << 3
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I have updated the PR to support using multiple bit shift operators in an expression and added additional tests.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
Looks like it is ready to include now, thanks.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@errael commented on this pull request.
> + if (iptr->isn_arg.op.op_type == EXPR_RSHIFT) + // clear the topmost sign bit + res &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);
Have you tried it without this post shift fixup? Since the type of the the left hand side is unsigned, no adjustment is needed.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@errael commented on this pull request.
> + if (iptr->isn_arg.op.op_type == EXPR_RSHIFT) + // clear the topmost sign bit + res &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS);Have you tried it without this post shift fixup? Since the type of the the left hand side is unsigned, no adjustment is needed.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()