cornell-zhang / heterocl

HeteroCL: A Multi-Paradigm Programming Infrastructure for Software-Defined Heterogeneous Computing
https://cornell-zhang.github.io/heterocl/
Apache License 2.0
322 stars 92 forks source link

[API][v0.3] Introduce Struct Data Type to HeteroCL #157

Closed seanlatias closed 4 years ago

seanlatias commented 4 years ago

In this PR, we introduce the C-like struct to HeteroCL.

Python Interface

Users can define a HeteroCL type by using hcl.struct with a Python dictionary. The following shows an example.

ts = hcl.Struct({"fa": hcl.Int(8), "fb": hcl.Fixed(8, 2), "fc": hcl.Float()})

Note that currently, we only support single-level structs. Namely, we cannot have a struct inside another struct. Since HeteroCL does not support pointers intrinsically, we will not have pointer members or self-referential structs.

Users can print the struct simply using Python print function. The following are some examples of getting information of a struct.

print(ts)
print(ts.fa)
print(ts["fa"])

With the support of a struct, we can create tensors having struct type. For example,

A = hcl.placeholder((10,), dtype=ts)
B = hcl.compute((10,), lambda x: 0, dtype=ts)

To access the elements, we provide C-like semantics (i.e., by using .). For example,

print(A[0].fa)
print(B[0].fb)

We can also initialize a tensor with struct type by using tuples. For example,

C = hcl.compute((10,), lambda x: (B[x].fa, B[x].fb, B[x].fc), dtype=ts)

Alternatively, we can use the imperative DSL provided by HeteroCL. For example,

with hcl.Stage():
  with hcl.for_(0, 10) as i:
    C[i].fa = B[i].fa
    C[i].fb = B[i].fb
    C[i].fc = B[i].fc

More examples can be seen in the tests.

CPU Simulation (LLVM JIT)

The struct can be run with CPU simulation. This is realized by using LLVM intrinsic bitcast. Note that for bitcast to work, we need to make sure the bitwidths are the same before and after typecast. Currently, this is guaranteed by the HeteroCL compiler. Thus, users only need to consider this requirement when they want to call the intrinsic directly from Python.

The struct type is treated as "unsigned int" for all internal representation.

HLS C Code Generation

For code generation, we consider two different conditions. First, if floating-point numbers are involved, we use the C++ union. Here is an example of a generated code, where A is a floating-point tensor while B is a struct with a floating-point field.

union { float from; uint32_t to; } _converter;
_converter.from = A[i];
B[i](31, 0) = _converter.to

If floating-point is not involved, we simply use the bit-selection provided by the HLS tool. Here, A is a tensor with type ap_fixed<8, 5> while B is a struct type.

ap_uint<8> _converter;
_converter(7, 0) = A[i](7, 0);
B[i](7, 0) = _converter;