Exemple #1
0
def store_results(stack_effect):
    '''
    Returns a Code to write the results from C variables into the stack,
    skipping 'ITEMS'.
     - stack_effect - StackEffect

    `stack_depth` must be modified first.
    '''
    code = Code()
    for item in stack_effect.results.items:
        if item.name != 'ITEMS':
            code.extend(store_item(item))
    return code
Exemple #2
0
def load_args(stack_effect):
    '''
    Returns a Code to read the arguments from the stack into C variables,
    skipping 'ITEMS' and 'COUNT'.
     - stack_effect - StackEffect

    `stack_depth` is not modified.
    '''
    code = Code()
    for item in stack_effect.args.items:
        if item.name != 'ITEMS' and item.name != 'COUNT':
            code.extend(load_item(item))
    return code
Exemple #3
0
    def add(self, depth_change):
        '''
        Returns a Code to update the variable `cached_depth` to reflect
        a change in the stack depth, e.g. by pushing or popping some items.
        Also updates `self`.

         - depth_change - int (N.B. not Size)
        '''
        assert type(depth_change) is int
        if depth_change == 0: return Code()
        self.cached_depth += depth_change
        if self.cached_depth < 0: self.cached_depth = 0
        self.checked_depth -= depth_change
        if self.checked_depth < 0: self.checked_depth = 0
        return Code(f'cached_depth = {self.cached_depth};')
Exemple #4
0
def run_body(instructions):
    '''
    Compute the instruction dispatch code for an inner run function.
    '''
    return dispatch(
        instructions,
        Code('// Undefined instruction.', 'THROW(MIT_ERROR_INVALID_OPCODE);'),
        gen_code=gen_instruction_code,
    )
Exemple #5
0
def run_inner_fn(instructions, suffix, instrument):
    '''
    Generate a `run_inner` function.

     - instructions - ActionEnum - instruction set.
     - suffix - str - the function is named `run_inner_{suffix}`.
     - instrument - Code or str - instrumentation to insert at start of main
       loop.
    '''
    return disable_warnings(
        ['-Wstack-protector', '-Wvla-larger-than='
         ],  # TODO: Stack protection cannot cope with VLAs.
        Code(
            f'''\
            // Define run_inner for the benefit of `call`.
            #define run_inner run_inner_{suffix}
            static void run_inner_{suffix}(mit_word_t *pc, mit_word_t ir, mit_word_t * restrict stack, mit_uword_t stack_words, mit_uword_t * restrict stack_depth_ptr, jmp_buf *jmp_buf_ptr)
            {{''',
            Code(*[
                '''\
                #define stack_depth (*stack_depth_ptr)
                mit_word_t error;

                for (;;) {''',
                instrument,
                Code('''\
                    uint8_t opcode = (uint8_t)ir;
                    ir = ARSHIFT(ir, 8);

                    // Check stack_depth is valid
                    if (stack_depth > stack_words)
                        THROW(MIT_ERROR_STACK_OVERFLOW);'''),
                run_body(instructions),
                '''\
                #undef stack_depth
                }''',
            ]),
            '''\
            error:
                longjmp(*jmp_buf_ptr, error);
            }
            #undef run_inner''',
        ))
Exemple #6
0
def declare_vars(stack_effect):
    '''
    Returns a Code to declare C variables for arguments and results other
    than 'ITEMS'.
     - stack_effect - StackEffect
    '''
    return Code(*[
        f'{item.type} {item.name};' for item in stack_effect.by_name.values()
        if item.name != 'ITEMS'
    ])
Exemple #7
0
def check_overflow(num_pops, num_pushes):
    '''
    Returns a Code to check that the stack contains enough space to
    push `num_pushes` items, given that `num_pops` items will first be
    popped successfully.
     - num_pops - Size.
     - num_pushes - Size.
    `num_pops` and `num_pushes` must both be variadic or both not.
    '''
    assert isinstance(num_pops, Size)
    assert isinstance(num_pushes, Size)
    assert num_pops >= 0
    assert num_pushes >= 0
    depth_change = num_pushes - num_pops
    if depth_change <= 0: return Code()
    # Ensure comparison will not overflow
    assert depth_change.count == 0
    return Code(f'''\
        if (unlikely(stack_words - stack_depth < {depth_change}))
            THROW(MIT_ERROR_STACK_OVERFLOW);''')
Exemple #8
0
    def store_results(self, results):
        '''
        Returns a Code to write the results from C variables into the
        stack. `stack_depth` must be modified first.

         - results - list of str.
        '''
        return Code(*[
            f'{self.lvalue(pos)} = {item.name};'
            for pos, item in enumerate(reversed(results.items))
        ])
Exemple #9
0
def check_underflow(num_pops):
    '''
    Returns a Code to check that the stack contains enough items to
    pop the specified number of items.
     - num_pops - Size, with non-negative `count`.
    '''
    assert isinstance(num_pops, Size)
    assert num_pops >= 0, num_pops
    if num_pops == 0: return Code()
    tests = []
    tests.append(f'unlikely(stack_depth < (mit_uword_t)({num_pops.size}))')
    if num_pops.count == 1:
        tests.append(
            f'unlikely(stack_depth - (mit_uword_t)({num_pops.size}) < (mit_uword_t)(COUNT))'
        )
    return Code(
        'if ({}) {{'.format(' || '.join(tests)),
        Code('THROW(MIT_ERROR_INVALID_STACK_READ);'),
        '}',
    )
Exemple #10
0
    def load_args(self, args):
        '''
        Returns a Code to read the arguments from the stack into C
        variables. `stack_depth` is not modified.

         - args - list of str.
        '''
        return Code(*[
            f'{item.name} = {self.lvalue(pos)};'
            for pos, item in enumerate(reversed(args.items))
        ])
Exemple #11
0
 def __init__(self, opcode, library, includes):
     super().__init__(Action(
         None,
         Code(
             '''\
         {
             mit_word_t function;''', pop_stack('function'), f'''
             int ret = trap_{self.name.lower()}(function, stack, &stack_depth);
             if (ret != 0)
                 THROW(ret);
         }}''')),
                      opcode=opcode)
     self.library = library
     self.includes = includes
Exemple #12
0
def gen_instruction_code(instruction):
    '''
    Generate a Code for an Instruction.

    This is suitable for passing as the `gen_code` argument of `dispatch()`.
    '''
    code = gen_action_code(instruction.action)
    if instruction.terminal is not None:
        ir_all_bits = 0 if instruction.opcode & 0x80 == 0 else -1
        code = Code(
            f'if (ir != {ir_all_bits}) {{',
            gen_action_code(instruction.terminal),
            '} else {',
            code,
            '}',
        )
    return code
Exemple #13
0
def run_fn(suffix):
    '''
    Generate a `mit_run`-like function.

     - suffix - str - the function is named `mit_run_{suffix}` and will call
       an inner function `run_inner_{suffix}`.
    '''
    return Code(
        f'''
        mit_word_t mit_run_{suffix}(mit_word_t *pc, mit_word_t ir, mit_word_t * restrict stack, mit_uword_t stack_words, mit_uword_t *stack_depth_ptr)
        {{
            jmp_buf env;
            mit_word_t error = (mit_word_t)setjmp(env);
            if (error == 0) {{
                run_inner_{suffix}(pc, ir, stack, stack_words, stack_depth_ptr, &env);
                error = MIT_ERROR_OK;
            }}
            return error;
        }}
        ''', )
Exemple #14
0
    def flush(self, goal=0):
        '''
        Decrease the number of stack items that are cached in C variables,
        if necessary. Returns a Code to move values between variables
        and to memory. Also updates the C variable `cached_depth`.

         - goal - a CacheState to match, or an int to specify a desired
           `cache_depth`. Default is `0`.
        '''
        if type(goal) is int:
            goal = CacheState(goal, self.checked_depth)
        assert goal.cached_depth <= self.cached_depth, (goal, self)
        assert goal.checked_depth <= self.checked_depth, (goal, self)
        self.checked_depth = goal.checked_depth
        if goal.cached_depth == self.cached_depth: return Code()
        code = Code()
        for pos in reversed(range(self.cached_depth)):
            code.append(f'{goal.lvalue(pos)} = {self.lvalue(pos)};')
        self.cached_depth = goal.cached_depth
        code.append(f'cached_depth = {self.cached_depth};')
        return code
Exemple #15
0
def store_stack(value, depth=0, type='mit_word_t'):
    '''
    Generate C code to store the value `value` of type `type` occupying
    stack slots `depth`, `depth+1`, ... . Does not check the stack.

    Returns a Code.
    '''
    code = Code()
    code.extend(
        disable_warnings(
            ['-Wpointer-to-int-cast', '-Wbad-function-cast'],
            Code(
                f'mit_max_stack_item_t temp = (mit_max_stack_item_t){value};'),
        ))
    for i in reversed(range(1, type_words(type))):
        code.append(
            f'*mit_stack_pos(stack, stack_depth, {depth + i}) = (mit_uword_t)(temp & MIT_UWORD_MAX);'
        )
        code.append('temp >>= MIT_WORD_BIT;')
    code.append(
        '*mit_stack_pos(stack, stack_depth, {}) = (mit_uword_t)({});'.format(
            depth,
            'temp & MIT_UWORD_MAX' if type_words(type) > 1 else 'temp',
        ))
    return Code('{', code, '}')
Exemple #16
0
def gen_case(instruction, cache_state):
    '''
    Generate a Code for a member of Instruction. It is the caller's
    responsibility to ensure that it's the right instruction to execute, and
    that the stack won't underflow or overflow.

    In the code, errors are reported by calling THROW(). When calling
    THROW(), the C variable `cached_depth` will contain the number of stack
    items cached in C locals.

     - instruction - Instructions.
     - cache_state - CacheState - Which StackItems are cached.
       Updated in place.
    '''
    code = Code()
    num_args = len(instruction.action.effect.args.items)
    num_results = len(instruction.action.effect.results.items)
    # Declare C variables for args and results.
    code.extend(Code(*[
        f'mit_word_t {name};'
        for name, item in instruction.action.effect.by_name.items()
    ]))
    # Load the arguments into their C variables.
    code.extend(cache_state.load_args(instruction.action.effect.args))
    # Inline `instruction.action.code`.
    # Note: `stack_depth` and `cache_state` must be correct for THROW().
    code.extend(instruction.action.code)
    # Update stack pointer and cache_state.
    code.append(f'stack_depth -= {num_args};')
    code.extend(cache_state.add(-num_args))
    code.extend(cache_state.add(num_results))
    code.append(f'stack_depth += {num_results};')
    # Store the results from their C variables.
    code.extend(cache_state.store_results(instruction.action.effect.results))
    return code
Exemple #17
0
class ExtraInstructions(ActionEnum):
    '''VM extra instructions.'''
    DIVMOD = (
        Action(
            StackEffect.of(['a', 'b'], ['q', 'r']),
            Code('''\
                if (b == 0)
                    THROW(MIT_ERROR_DIVISION_BY_ZERO);
                q = a / b;
                r = a % b;
            ''')),
        0x1,
    )

    UDIVMOD = (
        Action(
            StackEffect.of(['a', 'b'], ['q', 'r']),
            Code('''\
                if (b == 0)
                    THROW(MIT_ERROR_DIVISION_BY_ZERO);
                q = (mit_word_t)((mit_uword_t)a / (mit_uword_t)b);
                r = (mit_word_t)((mit_uword_t)a % (mit_uword_t)b);
            ''')),
        0x2,
    )

    THROW = (
        Action(
            None,  # Manage stack manually so that `stack_depth` is
            # decremented before THROW().
            Code('''\
                POP(n);
                THROW(n);
            '''),
        ),
        0x3,
    )

    CATCH = (
        Action(
            None,  # Manage stack manually because the stack module doesn't
            # understand multiple stack frames.
            Code('''\
                POP(addr);
                if (unlikely(addr % sizeof(mit_word_t) != 0))
                   THROW(MIT_ERROR_UNALIGNED_ADDRESS);
                DO_CATCH(addr);
            '''),
        ),
        0x4,
    )

    ARGC = (
        Action(
            StackEffect.of([], ['argc']),
            Code('argc = (mit_word_t)mit_argc;'),
        ),
        0x100,
    )

    ARGV = (
        Action(
            StackEffect.of([], ['argv:char **']),
            Code('argv = mit_argv;'),
        ),
        0x101,
    )
Exemple #18
0
        ),
        0x100,
    )

    ARGV = (
        Action(
            StackEffect.of([], ['argv:char **']),
            Code('argv = mit_argv;'),
        ),
        0x101,
    )


# Inject code for EXTRA
extra_code = Code('''\
    mit_uword_t extra_opcode = ir;
    ir = 0;
''')
extra_code.extend(
    dispatch(
        ExtraInstructions,
        Code('THROW(MIT_ERROR_INVALID_OPCODE);', ),
        'extra_opcode',
    ))


# Core instructions.
@dataclass
class Instruction:
    '''
    VM instruction descriptor.
Exemple #19
0
def push_stack(value, type='mit_word_t'):
    code = Code()
    code.extend(check_overflow(Size(0), Size(type_words(type))))
    code.extend(store_stack(value, depth=-type_words(type), type=type))
    code.append(f'stack_depth += {type_words(type)};')
    return code
Exemple #20
0
def load_stack(name, depth=0, type='mit_word_t'):
    '''
    Generate C code to load the variable `name` of type `type` occupying
    stack slots `depth`, `depth+1`, ... . Does not check the stack.

    Returns a Code.
    '''
    code = Code()
    code.append(
        f'mit_max_stack_item_t temp = (mit_uword_t)(*mit_stack_pos(stack, stack_depth, {depth}));'
    )
    for i in range(1, type_words(type)):
        code.append('temp <<= MIT_WORD_BIT;')
        code.append(
            f'temp |= (mit_uword_t)(*mit_stack_pos(stack, stack_depth, {depth + i}));'
        )
    code.extend(
        disable_warnings(
            ['-Wint-to-pointer-cast'],
            Code(f'{name} = ({type})temp;'),
        ))
    return Code('{', code, '}')
Exemple #21
0
def gen_action_code(action):
    '''
    Generate a Code for an Action.

    This is suitable for passing as the `gen_code` argument of `dispatch()`.
    '''
    effect = action.effect
    code = Code()
    if effect is not None:
        # Load the arguments into C variables.
        code.extend(declare_vars(effect))
        count = effect.args.by_name.get('COUNT')
        if count is not None:
            # If we have COUNT, check its stack position is valid, and load it.
            # We actually check `effect.args.size.size` (more than we need),
            # because this check will be generated anyway by the next
            # check_underflow call, so the compiler can elide one check.
            code.extend(check_underflow(Size(effect.args.size.size)))
            code.extend(load_item(count))
        code.extend(check_underflow(effect.args.size))
        code.extend(check_overflow(effect.args.size, effect.results.size))
        code.extend(load_args(effect))
    code.extend(action.code)
    if effect is not None:
        # Store the results from C variables.
        code.append(
            f'stack_depth += {effect.results.size - effect.args.size};')
        code.extend(store_results(effect))
    return code
Exemple #22
0
def dispatch(actions,
             undefined_case,
             opcode='opcode',
             gen_code=gen_action_code):
    '''
    Generate dispatch code for an ActionEnum.
     - actions - ActionEnum.
     - undefined_case - Code - the fallback behaviour.
     - opcode - str - a C expression for the opcode.
     - gen_code - function - a function that takes an ActionEnum instance and
       returns C code to implement it. In the code, errors are reported by
       calling THROW().
    '''
    assert isinstance(undefined_case, Code), undefined_case
    code = Code()
    else_text = ''
    for (_, value) in enumerate(actions):
        opcode_symbol = f'{c_symbol(actions.__name__)}_{value.name}'
        code.append(f'{else_text}if ({opcode} == {opcode_symbol}) {{')
        code.append(gen_code(value.action))
        code.append('}')
        else_text = 'else '
    code.append(f'{else_text}{{')
    code.append(undefined_case)
    code.append('}')
    return code
Exemple #23
0
class LibC(ActionEnum):
    'Function codes for the LIBC trap.'

    STRLEN = Action(
        StackEffect.of(['s:const char *'], ['len']),
        Code('''\
        len = (mit_word_t)(mit_uword_t)strlen(s);
    '''))

    STRNCPY = Action(
        StackEffect.of(['dest:char *', 'src:const char *', 'n'],
                       ['ret:char *']),
        Code('ret = strncpy(dest, src, (size_t)n);'),
    )

    STDIN = Action(StackEffect.of([], ['fd:int']),
                   Code('''\
        fd = (mit_word_t)STDIN_FILENO;
    '''))

    STDOUT = Action(StackEffect.of([], ['fd:int']),
                    Code('''\
        fd = (mit_word_t)STDOUT_FILENO;
    '''))

    STDERR = Action(StackEffect.of([], ['fd:int']),
                    Code('''\
        fd = (mit_word_t)STDERR_FILENO;
    '''))

    O_RDONLY = Action(StackEffect.of([], ['flag']),
                      Code('''\
        flag = (mit_word_t)O_RDONLY;
    '''))

    O_WRONLY = Action(StackEffect.of([], ['flag']),
                      Code('''\
        flag = (mit_word_t)O_WRONLY;
    '''))

    O_RDWR = Action(StackEffect.of([], ['flag']),
                    Code('''\
        flag = (mit_word_t)O_RDWR;
    '''))

    O_CREAT = Action(StackEffect.of([], ['flag']),
                     Code('''\
        flag = (mit_word_t)O_CREAT;
    '''))

    O_TRUNC = Action(StackEffect.of([], ['flag']),
                     Code('''\
        flag = (mit_word_t)O_TRUNC;
    '''))

    OPEN = Action(
        StackEffect.of(['str:char *', 'flags'], ['fd:int']),
        Code('''\
        fd = open(str, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (fd >= 0)
            set_binary_mode(fd, O_BINARY); // Best effort
    '''))

    CLOSE = Action(
        StackEffect.of(['fd:int'], ['ret:int']),
        Code('ret = (mit_word_t)close(fd);'),
    )

    READ = Action(
        StackEffect.of(['buf:void *', 'nbytes', 'fd:int'], ['nread:int']),
        Code('nread = read(fd, buf, nbytes);'),
    )

    WRITE = Action(
        StackEffect.of(['buf:void *', 'nbytes', 'fd:int'], ['nwritten']),
        Code('nwritten = write(fd, buf, nbytes);'),
    )

    SEEK_SET = Action(
        StackEffect.of([], ['whence']),
        Code('''\
        whence = (mit_word_t)SEEK_SET;
    '''))

    SEEK_CUR = Action(
        StackEffect.of([], ['whence']),
        Code('''\
        whence = (mit_word_t)SEEK_CUR;
    '''))

    SEEK_END = Action(
        StackEffect.of([], ['whence']),
        Code('''\
        whence = (mit_word_t)SEEK_END;
    '''))

    LSEEK = Action(
        StackEffect.of(['fd:int', 'offset:off_t', 'whence'], ['pos:off_t']),
        Code('pos = lseek(fd, offset, whence);'),
    )

    FDATASYNC = Action(
        StackEffect.of(['fd:int'], ['ret:int']),
        Code('ret = fdatasync(fd);'),
    )

    RENAME = Action(
        StackEffect.of(['old_name:char *', 'new_name:char *'], ['ret:int']),
        Code('ret = rename(old_name, new_name);'),
    )

    REMOVE = Action(StackEffect.of(['name:char *'], ['ret:int']),
                    Code('ret = remove(name);'))

    # TODO: Expose stat(2). This requires struct mapping!
    FILE_SIZE = Action(
        StackEffect.of(['fd:int'], ['size:off_t', 'ret:int']),
        Code('''\
            {
                struct stat st;
                ret = fstat(fd, &st);
                size = st.st_size;
            }
        '''),
    )

    RESIZE_FILE = Action(
        StackEffect.of(['size:off_t', 'fd:int'], ['ret:int']),
        Code('ret = ftruncate(fd, size);'),
    )

    FILE_STATUS = Action(
        StackEffect.of(['fd:int'], ['mode:mode_t', 'ret:int']),
        Code('''\
            {
                struct stat st;
                ret = fstat(fd, &st);
                mode = st.st_mode;
            }
        '''),
    )
Exemple #24
0
def pop_stack(name, type='mit_word_t'):
    code = Code()
    code.extend(check_underflow(Size(type_words(type))))
    code.extend(load_stack(name, type=type))
    code.append(f'stack_depth -= {type_words(type)};')
    return code