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

Implementing Shift instructions #41

Merged
merged 25 commits into from
Jun 27, 2024
Merged

Implementing Shift instructions #41

merged 25 commits into from
Jun 27, 2024

Conversation

jorbush
Copy link
Contributor

@jorbush jorbush commented Jun 21, 2024

Implementing Shift Instructions

Overview

This PR introduces the implementation and comprehensive testing for four bitwise shift and rotate operations: Shl (Shift Left), Shr (Shift Right), Rol (Rotate Left), and Ror (Rotate Right).

Progress and Testing Details

Each instruction (Shl, Shr, Rol, Ror) has the following:

  • Implementation (shift.rs): Defines the operation's behavior, either shifting or rotating bits.
  • Tests (integration_test.rs):
    • test_<op>_asm(): Verifies basic functionality using assembly code.
    • test_<op>_stack(): Checks the operation with stack-based addressing.
    • test_<op>_conditional_eq_set(): Confirms the operation sets the EQ flag.
    • test_<op>_set_eq_flag(): Ensures the EQ flag is set correctly for edge cases where the result is zero.
    • test_<op>_asm_greater_than_256(): Checks what happens when shifting with amounts greater than 256.

Conclusion

This PR ensures that the shift and rotate operations (Shl, Shr, Rol, Ror) are implemented and tested across various scenarios.

@jorbush jorbush self-assigned this Jun 21, 2024
@jorbush jorbush linked an issue Jun 21, 2024 that may be closed by this pull request
Closed
@jorbush jorbush marked this pull request as ready for review June 25, 2024 13:25
@jorbush
Copy link
Contributor Author

jorbush commented Jun 25, 2024

For ease of review, these are the specifications:

Shl (Shift Left)
  • Syntax:
    • shl in1, in2, out
    • shl.s in1, in2, out, to set swap modifier.
    • shl! in1, in2, out, to set set flags modifier.
    • shl.s! in1, in2, out, to set both swap and set flags modifiers.
  • Semantic:
    • Result is computed as in1 << (in2 % 256).
    • Flags are computed as follows:
      • EQ is set if result = 0.
      • Other flags are reset.
Shr (Shift Right)
  • Syntax:
    • shr in1, in2, out
    • shr.s in1, in2, out, to set swap modifier.
    • shr! in1, in2, out, to set set flags modifier.
    • shr.s! in1, in2, out, to set both swap and set flags modifiers.
  • Semantic:
    • Result is computed as in1 >> (in2 % 256).
    • Flags are computed as follows:
      • EQ is set if result = 0.
      • Other flags are reset.
Rol (Rotate Left)
  • Syntax:
    • rol in1, in2, out
    • rol.s in1, in2, out, to set swap modifier.
    • rol! in1, in2, out, to set set flags modifier.
    • rol.s! in1, in2, out, to set both swap and set flags modifiers.
  • Semantic:
    • Result is computed as in1 <<< (in2 % 256).
    • Flags are computed as follows:
      • EQ is set if result = 0.
      • Other flags are reset.
Ror (Rotate Right)
  • Syntax:
    • ror in1, in2, out
    • ror.s in1, in2, out, to set swap modifier.
    • ror! in1, in2, out, to set set flags modifier.
    • ror.s! in1, in2, out, to set both swap and set flags modifiers.
  • Semantic:
    • Result is computed as in1 >>> (in2 % 256).
    • Flags are computed as follows:
      • EQ is set if result = 0.
      • Other flags are reset.

programs/rol_stack.zasm Outdated Show resolved Hide resolved
tests/integration_test.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@gianbelinche gianbelinche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@fkrause98 fkrause98 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it looks good, let's discus a bit more some minor things and we're done.

fn _shl(vm: &mut VMState, opcode: &Opcode) {
let (src0_t, src1_t) = address_operands_read(vm, opcode);
let (src0, src1) = (src0_t.value, src1_t.value);
let shift = (src1.low_u32() % 256) as usize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying this is wrong per se, but I'm a bit confused.

The spec states that you take the lowest byte of the second input, here, you're taking the lowest 4 bytes, are we sure we aren't allowing something that shouldn't work based on the spec? We can also check how the fast vm is doing this to be completely sure.

Plus, I think the usize cast is redundant, why can't it simply be a u32?

Copy link
Contributor Author

@jorbush jorbush Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked fastVM and if we want something similar, we can use something like this instead:

let shift = src1.low_u32() as u8;

You are right that I get the last 4 bytes, but then I do the mod 256 and I get the last byte. I've done that so it's more aligned with the spec:

- The result is computed as in1 << (in2 % 256).

But I don't know the performance or readability of the code in this case, should I change it to the above alternative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the redundant usize, you were absolutely right, thanks!

Copy link
Contributor

@fkrause98 fkrause98 Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I don't know the performance or readability of the code in this case, should I change it to the above alternative?

As a rule of thumb, let's focus on simplicity+readability more than performance for now 🙂.

- The result is computed as in1 << (in2 % 256)
I'm a bit confused by the spec, it states you take the lowest byte as binary digits, so the max amount of shifts you can do
is 255, which is the range for numbers modulo 256. So the in2 % 256 looks a bit redundant.
But for now, I think following the spec as is is the best thing to do, so let's only take the second argument modulo 256,
and ignore the low 4 bytes, as it seems redundant.
Let's double check this with more people though, pinging @jrchatruc @IAvecilla @gianbelinche @juan518munoz.
Let's also have tests checking what happens when shifting with amounts greater than 256.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both src1.low_u32() and src1 % 256 would work.
I don't think the spec is redundant, since it asumes that in2 is a U256, so doing in2 % 256 is actually the way of getting the last byte

src/op_handlers/shift.rs Show resolved Hide resolved
src/op_handlers/shift.rs Show resolved Hide resolved
src/op_handlers/shift.rs Outdated Show resolved Hide resolved
tests/integration_test.rs Show resolved Hide resolved
@jorbush jorbush requested a review from fkrause98 June 26, 2024 11:56
src/lib.rs Outdated Show resolved Hide resolved
@juan518munoz juan518munoz merged commit a151eec into main Jun 27, 2024
3 checks passed
@juan518munoz juan518munoz deleted the shift-opcodes branch June 27, 2024 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Shift
4 participants