Skip to content

Commit

Permalink
Merge branch 'spcl:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
hodelcl authored Dec 13, 2023
2 parents 6a750e3 + 2dcd74a commit 62785f4
Show file tree
Hide file tree
Showing 130 changed files with 12,468 additions and 1,577 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/general-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7,'3.11']
python-version: [3.7,'3.12']
simplify: [0,1,autoopt]

steps:
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/pace-build-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: NASA/NOAA Pace repository build test

on:
workflow_dispatch:

defaults:
run:
shell: bash

jobs:
build_pace:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8.10]

steps:
- uses: actions/checkout@v2
with:
repository: '[email protected]:GEOS-ESM/pace.git'
ref: 'ci/DaCe'
submodules: 'recursive'
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies & pull correct DaCe
run: |
cd pace
python -m pip install --upgrade pip wheel setuptools
cd external/dace
git checkout ${{ github.sha }}
cd ../..
pip install -e external/gt4py
pip install -e external/dace
pip install -r requirements_dev.txt
- name: Download data
run: |
cd pace
mkdir -p test_data
cd test_data
wget https://portal.nccs.nasa.gov/datashare/astg/smt/pace-regression-data/8.1.3_c12_6_ranks_standard.D_SW.tar.gz
tar -xzvf 8.1.3_c12_6_ranks_standard.D_SW.tar.gz
wget https://portal.nccs.nasa.gov/datashare/astg/smt/pace-regression-data/8.1.3_c12_6_ranks_standard.RiemSolverC.tar.gz
tar -xzvf 8.1.3_c12_6_ranks_standard.RiemSolverC.tar.gz
wget https://portal.nccs.nasa.gov/datashare/astg/smt/pace-regression-data/8.1.3_c12_6_ranks_standard.Remapping.tar.gz
tar -xzvf 8.1.3_c12_6_ranks_standard.Remapping.tar.gz
cd ../..
- name: "Regression test: Riemman Solver on C-grid"
run: |
export FV3_DACEMODE=BuildAndRun
export PACE_CONSTANTS=GFS
cd pace
pytest -v -s --data_path=./test_data/8.1.3/c12_6ranks_standard/dycore \
--backend=dace:cpu --which_modules=Riem_Solver_C \
--threshold_overrides_file=./fv3core/tests/savepoint/translate/overrides/standard.yaml \
./fv3core/tests/savepoint
- name: "Regression test: D-grid shallow water lagrangian dynamics (D_SW)"
run: |
export FV3_DACEMODE=BuildAndRun
export PACE_CONSTANTS=GFS
cd pace
pytest -v -s --data_path=./test_data/8.1.3/c12_6ranks_standard/dycore \
--backend=dace:cpu --which_modules=D_SW \
--threshold_overrides_file=./fv3core/tests/savepoint/translate/overrides/standard.yaml \
./fv3core/tests/savepoint
- name: "Regression test: Remapping (on rank 0 only)"
run: |
export FV3_DACEMODE=BuildAndRun
export PACE_CONSTANTS=GFS
cd pace
pytest -v -s --data_path=./test_data/8.1.3/c12_6ranks_standard/dycore \
--backend=dace:cpu --which_modules=Remapping --which_rank=0 \
--threshold_overrides_file=./fv3core/tests/savepoint/translate/overrides/standard.yaml \
./fv3core/tests/savepoint
9 changes: 7 additions & 2 deletions dace/cli/sdfv.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ def view(sdfg: dace.SDFG, filename: Optional[Union[str, int]] = None):
"""
# If vscode is open, try to open it inside vscode
if filename is None:
if 'VSCODE_IPC_HOOK_CLI' in os.environ or 'VSCODE_GIT_IPC_HANDLE' in os.environ:
filename = tempfile.mktemp(suffix='.sdfg')
if (
'VSCODE_IPC_HOOK' in os.environ
or 'VSCODE_IPC_HOOK_CLI' in os.environ
or 'VSCODE_GIT_IPC_HANDLE' in os.environ
):
fd, filename = tempfile.mkstemp(suffix='.sdfg')
sdfg.save(filename)
os.system(f'code {filename}')
os.close(fd)
return

if type(sdfg) is dace.SDFG:
Expand Down
21 changes: 16 additions & 5 deletions dace/codegen/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import dace
from dace import dtypes
from dace import data
from dace.sdfg import SDFG
from dace.sdfg import SDFG, utils as sdutils
from dace.codegen.targets import framecode
from dace.codegen.codeobject import CodeObject
from dace.config import Config
Expand Down Expand Up @@ -165,6 +165,7 @@ def generate_code(sdfg, validate=True) -> List[CodeObject]:

if Config.get_bool('testing', 'serialization'):
from dace.sdfg import SDFG
import difflib
import filecmp
import shutil
import tempfile
Expand All @@ -174,10 +175,20 @@ def generate_code(sdfg, validate=True) -> List[CodeObject]:
sdfg2.save(f'{tmp_dir}/test2.sdfg', hash=False)
print('Testing SDFG serialization...')
if not filecmp.cmp(f'{tmp_dir}/test.sdfg', f'{tmp_dir}/test2.sdfg'):
shutil.move(f"{tmp_dir}/test.sdfg", "test.sdfg")
shutil.move(f"{tmp_dir}/test2.sdfg", "test2.sdfg")
raise RuntimeError('SDFG serialization failed - files do not match')

with open(f'{tmp_dir}/test.sdfg', 'r') as f1:
with open(f'{tmp_dir}/test2.sdfg', 'r') as f2:
diff = difflib.unified_diff(f1.readlines(),
f2.readlines(),
fromfile='test.sdfg (first save)',
tofile='test2.sdfg (after roundtrip)')
diff = ''.join(diff)
shutil.move(f'{tmp_dir}/test.sdfg', 'test.sdfg')
shutil.move(f'{tmp_dir}/test2.sdfg', 'test2.sdfg')
raise RuntimeError(f'SDFG serialization failed - files do not match:\n{diff}')

# Convert any loop constructs with hierarchical loop regions into simple 1-level state machine loops.
# TODO (later): Adapt codegen to deal with hierarchical CFGs instead.
sdutils.inline_loop_blocks(sdfg)

# Before generating the code, run type inference on the SDFG connectors
infer_types.infer_connector_types(sdfg)
Expand Down
7 changes: 4 additions & 3 deletions dace/codegen/compiled_sdfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def get_state_struct(self) -> ctypes.Structure:
return ctypes.cast(self._libhandle, ctypes.POINTER(self._try_parse_state_struct())).contents

def _try_parse_state_struct(self) -> Optional[Type[ctypes.Structure]]:
from dace.codegen.targets.cpp import mangle_dace_state_struct_name # Avoid import cycle
# the path of the main sdfg file containing the state struct
main_src_path = os.path.join(os.path.dirname(os.path.dirname(self._lib._library_filename)), "src", "cpu",
self._sdfg.name + ".cpp")
Expand All @@ -247,7 +248,7 @@ def _try_parse_state_struct(self) -> Optional[Type[ctypes.Structure]]:
code_flat = code.replace("\n", " ")

# try to find the first struct definition that matches the name we are looking for in the sdfg file
match = re.search(f"struct {self._sdfg.name}_t {{(.*?)}};", code_flat)
match = re.search(f"struct {mangle_dace_state_struct_name(self._sdfg)} {{(.*?)}};", code_flat)
if match is None or len(match.groups()) != 1:
return None

Expand Down Expand Up @@ -450,8 +451,8 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:
raise TypeError('Passing an object (type %s) to an array in argument "%s"' %
(type(arg).__name__, a))
elif dtypes.is_array(arg) and not isinstance(atype, dt.Array):
# GPU scalars are pointers, so this is fine
if atype.storage != dtypes.StorageType.GPU_Global:
# GPU scalars and return values are pointers, so this is fine
if atype.storage != dtypes.StorageType.GPU_Global and not a.startswith('__return'):
raise TypeError('Passing an array to a scalar (type %s) in argument "%s"' % (atype.dtype.ctype, a))
elif (not isinstance(atype, (dt.Array, dt.Structure)) and
not isinstance(atype.dtype, dtypes.callback) and
Expand Down
2 changes: 1 addition & 1 deletion dace/codegen/control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
x < 5
/------>[s2]--------\\
[s1] \ ->[s5]
[s1] \\ ->[s5]
------>[s3]->[s4]--/
x >= 5
Expand Down
46 changes: 32 additions & 14 deletions dace/codegen/cppunparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@
from dace import dtypes
from dace.codegen.tools import type_inference


if sys.version_info < (3, 8):
BytesConstant = ast.Bytes
EllipsisConstant = ast.Ellipsis
NameConstant = ast.NameConstant
NumConstant = ast.Num
StrConstant = ast.Str
else:
BytesConstant = ast.Constant
EllipsisConstant = ast.Constant
NameConstant = ast.Constant
NumConstant = ast.Constant
StrConstant = ast.Constant


# Large float and imaginary literals get turned into infinities in the AST.
# We unparse those infinities to INFSTR.
INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
Expand Down Expand Up @@ -574,7 +589,7 @@ def _generic_FunctionDef(self, t, is_async=False):
self.write('/* async */ ')

if getattr(t, "returns", False):
if isinstance(t.returns, ast.NameConstant):
if isinstance(t.returns, NameConstant):
if t.returns.value is None:
self.write('void')
else:
Expand Down Expand Up @@ -729,25 +744,26 @@ def _Repr(self, t):
raise NotImplementedError('Invalid C++')

def _Num(self, t):
repr_n = repr(t.n)
t_n = t.value if sys.version_info >= (3, 8) else t.n
repr_n = repr(t_n)
# For complex values, use DTYPE_TO_TYPECLASS dictionary
if isinstance(t.n, complex):
if isinstance(t_n, complex):
dtype = dtypes.DTYPE_TO_TYPECLASS[complex]

# Handle large integer values
if isinstance(t.n, int):
bits = t.n.bit_length()
if isinstance(t_n, int):
bits = t_n.bit_length()
if bits == 32: # Integer, potentially unsigned
if t.n >= 0: # unsigned
if t_n >= 0: # unsigned
repr_n += 'U'
else: # signed, 64-bit
repr_n += 'LL'
elif 32 < bits <= 63:
repr_n += 'LL'
elif bits == 64 and t.n >= 0:
elif bits == 64 and t_n >= 0:
repr_n += 'ULL'
elif bits >= 64:
warnings.warn(f'Value wider than 64 bits encountered in expression ({t.n}), emitting as-is')
warnings.warn(f'Value wider than 64 bits encountered in expression ({t_n}), emitting as-is')

if repr_n.endswith("j"):
self.write("%s(0, %s)" % (dtype, repr_n.replace("inf", INFSTR)[:-1]))
Expand Down Expand Up @@ -898,13 +914,13 @@ def _BinOp(self, t):
self.write(")")
# Special cases for powers
elif t.op.__class__.__name__ == 'Pow':
if isinstance(t.right, (ast.Num, ast.Constant, ast.UnaryOp)):
if isinstance(t.right, (NumConstant, ast.Constant, ast.UnaryOp)):
power = None
if isinstance(t.right, (ast.Num, ast.Constant)):
power = t.right.n
if isinstance(t.right, (NumConstant, ast.Constant)):
power = t.right.value if sys.version_info >= (3, 8) else t.right.n
elif isinstance(t.right, ast.UnaryOp) and isinstance(t.right.op, ast.USub):
if isinstance(t.right.operand, (ast.Num, ast.Constant)):
power = -t.right.operand.n
if isinstance(t.right.operand, (NumConstant, ast.Constant)):
power = - (t.right.operand.value if sys.version_info >= (3, 8) else t.right.operand.n)

if power is not None and int(power) == power:
negative = power < 0
Expand Down Expand Up @@ -984,7 +1000,9 @@ def _Attribute(self, t):
# Special case: 3.__abs__() is a syntax error, so if t.value
# is an integer literal then we need to either parenthesize
# it or add an extra space to get 3 .__abs__().
if (isinstance(t.value, (ast.Num, ast.Constant)) and isinstance(t.value.n, int)):
if isinstance(t.value, ast.Constant) and isinstance(t.value.value, int):
self.write(" ")
elif sys.version_info < (3, 8) and isinstance(t.value, ast.Num) and isinstance(t.value.n, int):
self.write(" ")
if (isinstance(t.value, ast.Name) and t.value.id in ('dace', 'dace::math', 'dace::cmath')):
self.write("::")
Expand Down
2 changes: 1 addition & 1 deletion dace/codegen/instrumentation/data/data_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def __init__(self):

def _generate_report_setter(self, sdfg: SDFG) -> str:
return f'''
DACE_EXPORTED void __dace_set_instrumented_data_report({sdfg.name}_t *__state, const char *dirpath) {{
DACE_EXPORTED void __dace_set_instrumented_data_report({cpp.mangle_dace_state_struct_name(sdfg)} *__state, const char *dirpath) {{
__state->serializer->set_folder(dirpath);
}}
'''
Expand Down
15 changes: 9 additions & 6 deletions dace/codegen/instrumentation/papi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from dace.sdfg.graph import SubgraphView
from dace.memlet import Memlet
from dace.sdfg import scope_contains_scope
from dace.sdfg.state import StateGraphView
from dace.sdfg.state import DataflowGraphView

import sympy as sp
import os
Expand Down Expand Up @@ -392,7 +392,7 @@ def should_instrument_entry(map_entry: EntryNode) -> bool:
return cond

@staticmethod
def has_surrounding_perfcounters(node, dfg: StateGraphView):
def has_surrounding_perfcounters(node, dfg: DataflowGraphView):
""" Returns true if there is a possibility that this node is part of a
section that is profiled. """
parent = dfg.entry_node(node)
Expand Down Expand Up @@ -448,7 +448,7 @@ class PAPIUtils(object):
def available_counters() -> Dict[str, int]:
"""
Returns the available PAPI counters on this machine. Only works on
\*nix based systems with ``grep`` and ``papi-tools`` installed.
*nix based systems with ``grep`` and ``papi-tools`` installed.
:return: A set of available PAPI counters in the form of a dictionary
mapping from counter name to the number of native hardware
Expand Down Expand Up @@ -605,7 +605,7 @@ def get_memlet_byte_size(sdfg: dace.SDFG, memlet: Memlet):
return memlet.volume * memdata.dtype.bytes

@staticmethod
def get_out_memlet_costs(sdfg: dace.SDFG, state_id: int, node: nodes.Node, dfg: StateGraphView):
def get_out_memlet_costs(sdfg: dace.SDFG, state_id: int, node: nodes.Node, dfg: DataflowGraphView):
scope_dict = sdfg.node(state_id).scope_dict()

out_costs = 0
Expand Down Expand Up @@ -636,7 +636,10 @@ def get_out_memlet_costs(sdfg: dace.SDFG, state_id: int, node: nodes.Node, dfg:
return out_costs

@staticmethod
def get_tasklet_byte_accesses(tasklet: nodes.CodeNode, dfg: StateGraphView, sdfg: dace.SDFG, state_id: int) -> str:
def get_tasklet_byte_accesses(tasklet: nodes.CodeNode,
dfg: DataflowGraphView,
sdfg: dace.SDFG,
state_id: int) -> str:
""" Get the amount of bytes processed by `tasklet`. The formula is
sum(inedges * size) + sum(outedges * size) """
in_accum = []
Expand Down Expand Up @@ -693,7 +696,7 @@ def get_memory_input_size(node, sdfg, state_id) -> str:
return sym2cpp(input_size)

@staticmethod
def accumulate_byte_movement(outermost_node, node, dfg: StateGraphView, sdfg, state_id):
def accumulate_byte_movement(outermost_node, node, dfg: DataflowGraphView, sdfg, state_id):

itvars = dict() # initialize an empty dict

Expand Down
22 changes: 20 additions & 2 deletions dace/codegen/targets/cpp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2021 ETH Zurich and the DaCe authors. All rights reserved.
# Copyright 2019-2023 ETH Zurich and the DaCe authors. All rights reserved.
"""
Helper functions for C++ code generation.
NOTE: The C++ code generator is currently located in cpu.py.
Expand All @@ -9,6 +9,7 @@
import itertools
import math
import numbers
import sys
import warnings

import sympy as sp
Expand All @@ -33,6 +34,22 @@
from dace.codegen.dispatcher import TargetDispatcher


def mangle_dace_state_struct_name(sdfg: Union[SDFG, str]) -> str:
"""This function creates a unique type name for the `SDFG`'s state `struct`.
The function uses the `compiler.codegen_state_struct_suffix`
configuration entry for deriving the type name of the state `struct`.
:param sdfg: The SDFG for which the name should be generated.
"""
name = sdfg if isinstance(sdfg, str) else sdfg.name
state_suffix = Config.get("compiler", "codegen_state_struct_suffix")
type_name = f"{name}{state_suffix}"
if not dtypes.validate_name(type_name):
raise ValueError(f"The mangled type name `{type_name}` of the state struct of SDFG '{name}' is invalid.")
return type_name


def copy_expr(
dispatcher,
sdfg,
Expand Down Expand Up @@ -1275,7 +1292,8 @@ def visit_BinOp(self, node: ast.BinOp):
evaluated_constant = symbolic.evaluate(unparsed, self.constants)
evaluated = symbolic.symstr(evaluated_constant, cpp_mode=True)
value = ast.parse(evaluated).body[0].value
if isinstance(evaluated_node, numbers.Number) and evaluated_node != value.n:
if isinstance(evaluated_node, numbers.Number) and evaluated_node != (
value.value if sys.version_info >= (3, 8) else value.n):
raise TypeError
node.right = ast.parse(evaluated).body[0].value
except (TypeError, AttributeError, NameError, KeyError, ValueError, SyntaxError):
Expand Down
Loading

0 comments on commit 62785f4

Please sign in to comment.