Example #1
0
def parseLoop(nodes: List[Element], loc: Location) -> OP.Loop:
  if len(nodes) < 3:
    raise ParseError('incorrect number of args passed to op_par_loop', loc)

  # Parse loop kernel and set
  kernel = parseIdentifier(nodes[0])
  set_   = parseIdentifier(nodes[1])

  loop_args = []

  # Parse loop args
  for raw_arg in nodes[2:]: 
    name = parseIdentifier(raw_arg)
    arg_loc = parseLocation(raw_arg)
    args = raw_arg.findall('name/subscripts/subscript')

    if name == 'op_arg_dat':
      loop_args.append(parseArgDat(args, arg_loc))

    elif name == 'op_opt_arg_dat':
      loop_args.append(parseOptArgDat(args, arg_loc))

    elif name == 'op_arg_gbl':
      loop_args.append(parseArgGbl(args, arg_loc))

    elif name == 'op_opt_arg_gbl':
      loop_args.append(parseOptArgGbl(args, arg_loc))

    else:
      raise ParseError(f'invalid loop argument {name}')

  return OP.Loop(kernel, set_, loc, loop_args)
Example #2
0
def parseIntLit(node: Element, signed: bool = True) -> int:
  # Parse location
  loc = parseLocation(node)

  # Assume the literal is not negated
  negation = False

  # Check if the node is wrapped in a valid unary negation
  if signed and node.find('operation'):
    node = node.find('operation')
    if node.attrib['type'] == 'unary' and node.find('operator') and node.find('operator').attrib['operator'] == '-':
      negation = True
      node = node.find('operand')

  # Descend to child literal node
  node = node.find('literal')

  # Verify and typecheck the literal node
  if not node or node.attrib['type'] != 'int':
    if not signed:
      raise ParseError('expected unsigned integer literal', loc)
    else:
      raise ParseError('expected integer literal', loc)

  # Extract the value
  value = int(node.attrib['value'])

  return -value if negation else value
Example #3
0
def parseLoop(nodes: List[Cursor], loc: Location) -> OP.Loop:
    if len(nodes) < 3:
        raise ParseError('incorrect number of args passed to op_par_loop')

    # Parse loop kernel and set
    kernel = parseIdentifier(nodes[0])
    _ = parseStringLit(nodes[1])
    set_ = parseIdentifier(nodes[2])

    loop_args = []

    # Parse loop args
    for node in nodes[3:]:
        node = descend(descend(node))
        name = node.spelling
        arg_loc = parseLocation(node)
        args = list(node.get_children())[1:]

        if name == 'op_arg_dat':
            loop_args.append(parseArgDat(args, arg_loc))

        elif name == 'op_opt_arg_dat':
            loop_args.append(parseOptArgDat(args, arg_loc))

        elif name == 'op_arg_gbl':
            loop_args.append(parseArgGbl(args, arg_loc))

        elif name == 'op_opt_arg_gbl':
            loop_args.append(parseOptArgGbl(args, arg_loc))

        else:
            raise ParseError(f'invalid loop argument {name}',
                             parseLocation(node))

    return OP.Loop(kernel, set_, loc, loop_args)
Example #4
0
def parseIdentifier(node: Cursor, regex: str = None) -> str:
    # TODO: Check this
    while node.kind == CursorKind.CSTYLE_CAST_EXPR:
        node = list(node.get_children())[1]

    # Descend to child node
    if node.kind == CursorKind.UNEXPOSED_EXPR:
        node = descend(node)

    # Descend to child node
    if node.kind == CursorKind.UNARY_OPERATOR and next(
            node.get_tokens()).spelling in ('&', '*'):
        node = descend(node)

    # Check for null
    if node.kind == CursorKind.GNU_NULL_EXPR:
        return ''

    # Validate the node
    if node.kind != CursorKind.DECL_REF_EXPR:
        raise ParseError('expected identifier')

    value = node.spelling

    # Apply conditional regex constraint
    if regex and not re.match(regex, value):
        raise ParseError(f'expected identifier matching {regex}')

    return value
Example #5
0
def parseKernel(self, path: Path, name: str) -> Kernel:
    # Invoke Clang parser on kernel source
    translation_unit = Index.create().parse(path)

    # Collect root-level nodes
    nodes = translation_unit.cursor.get_children()

    # Search for kernel function
    node = safeFind(
        nodes,
        lambda n: n.kind == CursorKind.FUNCTION_DECL and n.spelling == name)
    if not node:
        raise ParseError(f'failed to locate kernel function {name}',
                         parseLocation(node))

    # Collect parameter types
    params = []
    for n in node.get_children():
        if n.kind == CursorKind.PARM_DECL:
            type = n.type.get_pointee() or n.type
            type = re.sub(r'\s*const\s*', '', type.spelling)
            param = (n.spelling, type)
            params.append(param)

    return Kernel(name, path, translation_unit.cursor, params)
Example #6
0
def parseKernel(self, path: Path, name: str) -> Kernel:  
  # Parse AST
  ast = parse(path)

  # Search for kernel function
  nodes = ast.findall('file/subroutine')
  node = safeFind(nodes, lambda n: n.attrib['name'] == name)
  if not node:
    raise ParseError(f'failed to locate kernel function {name}')

  # Parse parameter identifiers
  param_identifiers = [ n.attrib['name'] for n in node.findall('header/arguments/argument') ]
  params = [ ('', '') ] * len(param_identifiers)

  # TODO: Cleanup
  for decl in node.findall('body/specification/declaration'):
    if decl.attrib and decl.attrib['type'] == 'variable':
      type = decl.find('type')
      for variable in decl.findall('variables/variable'):
        if variable.attrib:
          identifier = variable.attrib['name']
          if identifier in param_identifiers:
            index = param_identifiers.index(identifier)
            params[index] = (identifier, parseType(type))
            
  return Kernel(name, path, ast, params)
Example #7
0
def parseSet(nodes: List[Element], loc: Location) -> OP.Set:
  if len(nodes) != 3:
    raise ParseError('incorrect number of nodes passed to op_decl_set', loc)

  _     = parseIdentifier(nodes[0])
  ptr   = parseIdentifier(nodes[1])
  debug = parseStringLit(nodes[2])
  
  return OP.Set(ptr)
Example #8
0
def parseConst(nodes: List[Element], loc: Location) -> OP.Const:
  if len(nodes) != 3:
    raise ParseError('incorrect number of args passed to op_decl_const', loc)

  ptr  = parseIdentifier(nodes[0])
  dim   = parseIntLit(nodes[1], signed=False)
  debug = parseStringLit(nodes[2])

  return OP.Const(ptr, dim, debug, loc)
Example #9
0
def parseIdentifier(node: Element, regex: str = None) -> str:
  # Parse location
  loc = parseLocation(node)

  # Descend to child node
  node = node.find('name')

  # Validate the node
  if not node or not node.attrib['id']:
    raise ParseError('expected identifier', loc)

  value = node.attrib['id']

  # Apply conditional regex constraint
  if regex and not re.match(regex, value):
    raise ParseError(f'expected identifier matching {regex}', loc)
  
  return value
Example #10
0
def parseStringLit(node: Element, regex: str = None) -> str:
  # Parse location
  loc = parseLocation(node)

  # Descend to child literal node
  node = node.find('literal')

  # Validate the node
  if not node or node.attrib['type'] != 'char':
    raise ParseError('expected string literal', loc)

  # Extract value from string delimeters
  value = node.attrib['value'][1:-1]

  # Apply conditional regex constraint
  if regex and not re.match(regex, value):
    raise ParseError(f'expected string literal matching {regex}', loc)

  return value
Example #11
0
def parse(path: Path) -> Element:
  try:
    # Track the current file for parse errors
    global _current_file
    _current_file = str(path)

    # Invoke OFP on the source
    return fp.parse(path, raise_on_error=True)
  except CalledProcessError as error:
    raise ParseError(error.output)
Example #12
0
def parseMap(nodes: List[Cursor], ptr: str, loc: Location) -> OP.Map:
    if len(nodes) != 5:
        raise ParseError('incorrect number of args passed to op_decl_map', loc)

    from_set = parseIdentifier(nodes[0])
    to_set = parseIdentifier(nodes[1])
    dim = parseIntLit(nodes[2], signed=False)
    _ = parseIdentifier(nodes[3])
    debug = parseStringLit(nodes[4])

    return OP.Map(from_set, to_set, dim, ptr, loc)
Example #13
0
def parseData(nodes: List[Cursor], ptr: str, loc: Location) -> OP.Data:
    if len(nodes) != 5:
        raise ParseError('incorrect number of args passed to op_decl_dat', loc)

    set_ = parseIdentifier(nodes[0])
    dim = parseIntLit(nodes[1], signed=False)
    typ = parseStringLit(nodes[2])
    _ = parseIdentifier(nodes[3])
    debug = parseStringLit(nodes[4])

    return OP.Data(set_, dim, typ, ptr, loc)
Example #14
0
def parseStringLit(node: Cursor, regex: str = None) -> str:
    # Validate the node
    if node.kind != CursorKind.UNEXPOSED_EXPR:
        raise ParseError('expected string literal')

    # Descend to child node
    node = descend(node)

    # Validate the node
    if node.kind != CursorKind.STRING_LITERAL:
        raise ParseError('expected string literal')

    # Extract value from string delimeters
    value = node.spelling[1:-1]

    # Apply conditional regex constraint
    if regex and not re.match(regex, value):
        raise ParseError(f'expected string literal matching {regex}')

    return value
Example #15
0
def parseArgGbl(nodes: List[Element], loc: Location) -> OP.Arg:
  if len(nodes) != 4:
    raise ParseError('incorrect number of args passed to op_arg_gbl', loc)

  access_regex = enumRegex(OP.GBL_ACCESS_TYPES)
  
  var = parseIdentifier(nodes[0])
  dim = parseIntLit(nodes[1], signed=False)
  typ = normaliseType(parseStringLit(nodes[2]))
  acc = parseIdentifier(nodes[3], regex=access_regex)

  return OP.Arg(var, dim, typ, acc, loc)
Example #16
0
def parseData(nodes: List[Element], loc: Location) -> OP.Data:
  if len(nodes) != 6:
    raise ParseError('incorrect number of args passed to op_decl_dat', loc)

  set_  = parseIdentifier(nodes[0])
  dim   = parseIntLit(nodes[1], signed=False)
  typ   = normaliseType(parseStringLit(nodes[2]))
  _     = parseIdentifier(nodes[3])
  ptr   = parseIdentifier(nodes[4])
  debug = parseStringLit(nodes[5])

  return OP.Data(set_, dim, typ, ptr, loc)
Example #17
0
def parseIntLit(node: Cursor, signed: bool = True) -> int:
    # Assume the literal is not negated
    negation = False

    # Check if the node is wrapped in a valid unary negation
    if signed and node.kind == CursorKind.UNARY_OPERATOR and next(
            node.get_tokens()).spelling == '-':
        negation = True
        node = descend(node)

    # Validate the node
    if node.kind != CursorKind.INTEGER_LITERAL:
        if not signed:
            raise ParseError('expected unsigned integer literal')
        else:
            raise ParseError('expected integer literal')

    # Extract the value
    value = int(next(node.get_tokens()).spelling)

    return -value if negation else value
Example #18
0
def parseOptArgGbl(nodes: List[Element], loc: Location) -> OP.Arg:
  if len(nodes) != 5:
    ParseError('incorrect number of args passed to op_opt_arg_gbl', loc)

  # Parse opt argument
  opt = parseIdentifier(nodes[0])

  # Parse standard argGbl arguments
  dat = parseArgGbl(nodes[1:], loc)
  
  # Return augmented dat
  dat.opt = opt
  return dat
Example #19
0
def parseArgGbl(nodes: List[Cursor], loc: Location) -> OP.Arg:
    if len(nodes) != 4:
        raise ParseError('incorrect number of args passed to op_arg_gbl', loc)

    access_regex = enumRegex(OP.GBL_ACCESS_TYPES)

    var = parseIdentifier(nodes[0])
    dim = parseIntLit(nodes[1], signed=False)
    typ = parseStringLit(nodes[2])
    acc = macro_instances[(nodes[3].location.line,
                           nodes[3].location.column)]  # TODO: Cleanup

    return OP.Arg(var, dim, typ, acc, loc)
Example #20
0
def parseArgDat(nodes: List[Cursor], loc: Location) -> OP.Arg:
    if len(nodes) != 6:
        raise ParseError('incorrect number of args passed to op_arg_dat', loc)

    access_regex = enumRegex(OP.DAT_ACCESS_TYPES)

    var = parseIdentifier(nodes[0])
    idx = parseIntLit(nodes[1], signed=True)
    map_ = parseIdentifier(nodes[2]) or OP.ID
    dim = parseIntLit(nodes[3], signed=False)
    typ = parseStringLit(nodes[4])
    acc = macro_instances[(nodes[5].location.line,
                           nodes[5].location.column)]  # TODO: Cleanup

    return OP.Arg(var, dim, typ, acc, loc, map_, idx)
Example #21
0
def parseProgram(self, path: Path, include_dirs: Set[Path]) -> Program:
    # Locate OP2 install
    op2_install = os.getenv('OP2_INSTALL_PATH')
    if not op2_install:
        exit('Fatal: OP2_INSTALL_PATH not set')

    # Add OP2 includes
    op2_include = Path(op2_install).joinpath('c/include')
    include_dirs.add(op2_include)

    # Form Clang args
    args = [f'-I{dir}' for dir in include_dirs]

    # Invoke Clang parser on the program source
    translation_unit = Index.create().parse(
        path,
        args=args,
        options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD)

    # Throw the parse error first parse error caught in the diagnostics
    error = next(iter(translation_unit.diagnostics), None)
    if error:
        raise ParseError(error.spelling, parseLocation(error))

    # Initialise search stack
    program = Program(path)
    stack = []

    for child in translation_unit.cursor.get_children():
        # Ignore macro definitions and cursors outside of the program file
        if child.kind != CursorKind.MACRO_DEFINITION and child.location.file.name == translation_unit.spelling:
            # Collect the locations and identifiers of macro instances
            if child.kind == CursorKind.MACRO_INSTANTIATION:
                macro_instances[(child.location.line,
                                 child.location.column)] = child.displayname
            # Populate with the top-level cursors from the target file
            else:
                stack.append(child)

    ptr: str

    # DFS
    while stack:
        # Manage the stack
        node = stack.pop()
        stack.extend(node.get_children())

        # TODO: This is a bit of a hack. Cleanup
        if node.kind == CursorKind.VAR_DECL:
            if safeFind(node.get_children(),
                        lambda n: n.kind == CursorKind.CALL_EXPR):
                ptr = node.spelling

        # Focus on function calls
        if node.kind == CursorKind.CALL_EXPR:
            name = node.spelling
            args = list(node.get_children())[1:]
            loc = parseLocation(node)

            if name == 'op_init':
                program.recordInit(loc)

            elif name == 'op_decl_set':
                program.sets.append(parseSet(args, ptr, loc))

            elif name == 'op_decl_map':
                program.maps.append(parseMap(args, ptr, loc))

            elif name == 'op_decl_dat':
                program.datas.append(parseData(args, ptr, loc))

            elif name == 'op_decl_const':
                program.consts.append(parseConst(args, loc))

            elif name == 'op_par_loop':
                program.loops.append(parseLoop(args, loc))

            elif name == 'op_exit':
                program.recordExit()

    return program