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

add/sub -> rtype gadget to build circuit #230

Closed
wants to merge 1 commit into from

Conversation

hero78119
Copy link
Collaborator

@hero78119 hero78119 commented Sep 16, 2024

This PR aiming for

  • circuit construction/assignment by sharing gadgets, aiming for removing boilerplate code. with that we can implement all type of R opcodes with most of logic shared.
  • reuse riv32m instruction definition from ceno_emul

related to #219 and #228

let isa_idx = self.table[Self::map10(decoded.opcode, decoded.func3, decoded.func7)];
RV32IM_ISA[isa_idx as usize]
}

pub const fn lookup_raw(&self, opcode: u32, func3: u32, func7: u32) -> FastDecodeEntry {
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 we can leverage InsnKind directly and then we don't need to assign opcode, func3 and func7.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

you are right! I force-push to update it with more simplify design 👍

@naure
Copy link
Collaborator

naure commented Sep 16, 2024

I’ve doing something related over here: #231

pub lt_rs1_cfg: ExprLtConfig,
pub lt_rs2_cfg: ExprLtConfig,
pub lt_prev_ts_cfg: ExprLtConfig,
phantom: PhantomData<E>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

phantom is not necessary.

@@ -282,7 +282,7 @@ const fn insn(
type InstructionTable = [FastDecodeEntry; 48];
type FastInstructionTable = [u8; 1 << 10];

const RV32IM_ISA: InstructionTable = [
pub const RV32IM_ISA: InstructionTable = [
Copy link
Collaborator

Choose a reason for hiding this comment

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

There could be a pub fn getter and keep details of the table private.


#[derive(Debug)]
pub struct RTypeInstructionConfig<E: ExtensionField> {
pub pc: WitIn,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Such a complex type should have private fields. Only the 3 operands are needed outside, and they could be exposed by pub fn methods or passed as arguments to operands_fn.

@@ -7,10 +7,12 @@ pub mod addsub;
pub mod blt;
pub mod config;
pub mod constants;
mod gadgets;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could have a specific name such as r_insn.

pub lt_rs2_cfg: ExprLtConfig,
pub lt_prev_ts_cfg: ExprLtConfig,
phantom: PhantomData<E>,
pub struct InstructionConfig<E: ExtensionField>(RTypeInstructionConfig<E>);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This type does not really bring anything, it could be type InstructionConfig = RTypeInstructionConfig directly.

instance: &mut [MaybeUninit<E::BaseField>],
lk_multiplicity: &mut LkMultiplicity,
step: &StepRecord,
operands_fn: impl FnOnce(
Copy link
Collaborator

@naure naure Sep 16, 2024

Choose a reason for hiding this comment

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

I think this function should be symmetrical with the one in construct_circuit. Here it is strange that this function is given the whole config, and implicitly it must assign some fields and not some others from the config.

Instead, we say specifically that the inheriting circuit (e.g. Add) is responsible for the operands. In construct_circuit it allocates them, and in assign it assigns them. This responsibility can be made explicit by passing back the operands that it created and not the rest.

        operands_fn: impl FnOnce(
            &RegUInt<E>, // operand_1
            &RegUInt<E>, // operand_2
            &RegUInt<E>, // operand_out
            &mut [MaybeUninit<E::BaseField>],
            &mut LkMultiplicity,
            &StepRecord,
        ) -> Result<(), ZKVMError>,

Copy link
Collaborator

Choose a reason for hiding this comment

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

Alternatively, there is the composition style (see #231), where the operands stay in the specific type that owns them and not in the gadget.

lk_multiplicity.fetch(step.pc().before.0);
set_val!(instance, config.pc, step.pc().before.0 as u64);
set_val!(instance, config.ts, step.cycle());
let addend_1 = UIntValue::new_unchecked(step.rs2().unwrap().value);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Leave the operand responsibility to the caller (operand_fn).

@hero78119
Copy link
Collaborator Author

Thanks for all the nice comments @naure
Per discuss offline, we can switch to alternative #231 for better implementation :0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants