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

No Modulo / Remainder Operator "%" for integer types #202

Open
Paulo-D2000 opened this issue Jul 2, 2024 · 2 comments
Open

No Modulo / Remainder Operator "%" for integer types #202

Paulo-D2000 opened this issue Jul 2, 2024 · 2 comments

Comments

@Paulo-D2000
Copy link
Contributor

Paulo-D2000 commented Jul 2, 2024

The integer modulo (remainder) Operator "%" insn't implemented...

Demo code:

#include "intN_t.h"

#pragma MAIN_MHZ test 100.0
int32_t test(int32_t a, int32_t b){
    return a % b; // not implemented
}

Tool Output:
GET_BIN_OP_MOD_C_CODE Only mod between float for now!

Possible [Slow | Non-Generic] Fix is overloading the operator:

int32_t BIN_OP_MOD_int32_t_int32_t(int32_t left, int32_t right){
    return left - ((left / right) * right);
}

This generates:
BIN_OP_DIV_int32_t_int32_t BIN_OP_MINUS_uint32_t_uint1_t UNARY_OP_NOT_uint32_t MUX_uint1_t_uint32_t_uint32_t BIN_OP_DIV_uint32_t_uint32_t BIN_OP_GTE_uint32_t_uint32_t BIN_OP_MINUS_int33_t_int33_t UNARY_OP_NOT_uint1_t BIN_OP_MINUS_uint32_t_uint32_t BIN_OP_XOR_uint1_t_uint1_t MUX_uint1_t_int32_t_int32_t UNARY_OP_NEGATE_uint32_t UNARY_OP_NOT_uint33_t BIN_OP_PLUS_uint33_t_uint1_t BIN_OP_INFERRED_MULT_int32_t_int32_t BIN_OP_MINUS_int32_t_int64_t

@JulianKemmerer
Copy link
Owner

My log of notes in implementing this
Run code trying to use %
Get error
GET_BIN_OP_MOD_C_CODE Only mod between float for now!
Find error message in .py
inside function
def GET_BIN_OP_MOD_C_CODE(partially_complete_logic, out_dir):

This function is saying
given some function defintion of comb logic that is partially filled out
(know func signature, not the body)
return the PipelineC code that implements the % operation in terms of simpler ops
partially_complete_ meaning
ex. we know the input types
looking them up by input argument name string

left_t = partially_complete_logic.wire_to_c_type[partially_complete_logic.inputs[0]]
right_t = partially_complete_logic.wire_to_c_type[partially_complete_logic.inputs[1]]

Which leads us to error out if the types arent floats atm.

Inside def GET_BIN_OP_MOD_C_CODE(partially_complete_logic, out_dir):
happens to be some commented out code
that calls GET_BIN_OP_DIV_UINT_N_C_CODE(partially_complete_logic, out_dir)
that is past me saying - do something like was done for DIV

Notice DIV_UINT_N for that func, its typed, since uint div without sign bit handled was done first, signed after, inefficient duplicated code, etc meh

Lets do signed integers first actually

There is also commented out code that was checking types too
if VHDL.WIRES_ARE_UINT_N(partially_complete_logic.inputs, partially_complete_logic)
asking for a list of wire names in some comb logic , are all those wires uint?

well use the signed version ARE_INT_N

if VHDL.WIRES_ARE_INT_N(partially_complete_logic.inputs, partially_complete_logic):
        return GET_BIN_OP_MOD_INT_N_C_CODE(partially_complete_logic, out_dir)

and change the error message to say only for floats and ints for now...

So we introduced GET_BIN_OP_MOD_INT_N_C_CODE where should that live?
Again, doing something like DIV, lets find GET_BIN_OP_DIV_INT_N_C_CODE
and copy that as a start
def GET_BIN_OP_DIV_INT_N_C_CODE(partially_complete_logic, out_dir, parser_state):
to
def GET_BIN_OP_MOD_INT_N_C_CODE(partially_complete_logic, out_dir, parser_state):

At this point some point in past forget to get the parser_state arg propogated down into this func call, minor change to note that was fixed.

After copying GET_BIN_OP_DIV_INT_N_C_CODE we have a function that generates PipelineC to do divison.
It generates code to remove the sign bit, convert to absolute value unsigned, do unsigned div, and then fix sign bit at the end
Hopefully MOD fits into doing that and doesnt need custom implementation for signed MOD not based on unsigned MOD...
So for now, in that copied code just changing the division /, etc stuff to a % and will revisit.

Running pipelinec on the original example code again
youll see it elaborate % for signed into absolute value stuff
and then fail printing our modified error message
GET_BIN_OP_MOD_C_CODE Only mod between float and ints for now!

Back into GET_BIN_OP_MOD_C_CODE
we will add the unsigned version

    elif VHDL.WIRES_ARE_UINT_N(partially_complete_logic.inputs, partially_complete_logic):
        return GET_BIN_OP_MOD_UINT_N_C_CODE(partially_complete_logic, out_dir, parser_state)

and change error message to something like not floats or ints wtf?
make it an exception too why not....
raise Exception(f"Modulo between unknown types {left_t} % {right_t}?")
dont have source available in the section of compiler to point to where specifically in code the modulo is
if wanted to do that, it is done in upper layers that for ex. generated partially_complete_logic to start with

Similar to before
copy the DIV version to start
def GET_BIN_OP_DIV_UINT_N_C_CODE(partially_complete_logic, out_dir, parser_state):
rename it GET_BIN_OP_MOD_UINT_N_C_CODE

This function generates PipelineC code to do unsigned division like
https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division

it was written before PipelineC supported for loops
and so the loops are done in python to unroll and print verbose generated lines of C code
and would you look at that, there was already a remainder variable in that DIV code
its just unused / not output at the end
(maybe should make some built in div_mod func that returns both values from one op?)

but now our MOD func will output the remainder instead of the DIV output
return remainder; is pretty much the only difference from DIV generated code
this is absolutely another reminder about the TODOs for sharing more of the DIV and MOD code gen, needlessly copied but outside scope of just implementing mod...

Running the example code again now
youll see the component pieces being found

Elaborating main function hierarchies down to raw HDL logic...
... found: int32_abs
... found: BIN_OP_MOD_int32_t_int32_t
... found: BIN_OP_MOD_uint32_t_uint32_t

In the output directory you can see the generated C code for those ops as described in the python:
pipelinec_output/built_in/BIN_OP_MOD_int32_t_int32_t/BIN_OP_MOD_int32_t_int32_t.c

#include "intN_t.h"
#include "uintN_t.h"
#include "bit_manip.h"

// 32b % 32b mod
int32_t BIN_OP_MOD_int32_t_int32_t(int32_t left, int32_t right)
{
  
  // Record sign bits
  uint1_t l_signed = int32_31_31(left);
  uint1_t r_signed = int32_31_31(right);

  // Resize to unsigned values of same width 
  uint32_t left_resized = int32_abs(left);
  uint32_t right_resized = int32_abs(right);

  // Do mod on uints
  uint32_t unsigned_result = left_resized % right_resized;

  // Adjust sign
  int32_t output = unsigned_result;
  if(l_signed ^ r_signed)
  {
    output = -unsigned_result;
  }

  return output;
}

And should be done assuming the sign bit fixing using unsigned mod to do signed mod makes sense...

@JulianKemmerer
Copy link
Owner

09fb8a3

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

No branches or pull requests

2 participants