zeam-vm / pelemay

Pelemay is a native compiler for Elixir, which generates SIMD instructions. It has a plan to generate for GPU code.
Apache License 2.0
186 stars 13 forks source link

Implement a new type code generator #70

Open zacky1972 opened 5 years ago

zacky1972 commented 5 years ago

Suppose the following ZEAM IR is given:

[{:include, [], "stdbool.h"}, 
 {:include, [], "erl_nif.h"},
 {:define_const_int, [], [{:=, [], [{:FAIL, [], :macro}, 0]}]},
 {:define_const_int, [], [{:=, [], [{:SUCCESS, [], :macro}, 1]}]},
 {:defunc, :add, [
  {:raise_badarg_if, [{:!=, :argc, 2}]},
  {:let, [
    {:a, {:return_if_fail, {:get_int64, {:get_array, :argv, 0}}}},
    {:b, {:return_if_fail, {:get_int64, {:get_array, :argv, 1}}}},
    ], {
    {:make_int64, {:add_int64, :a, :b}}
  }}
]}]

It will be converted into the following C code:

#include <stdbool.h>
#include <erl_nif.h>
#define FAIL 0
#define SUCCESS 1

static ERL_NIF_TERM
add(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[])
{
  if (__builtin_expect((argc != 2), false)) {
    return enif_make_badarg(env);
  }
  {
    long a;
    if (__builtin_expect((enif_get_int64(env, argv[0], &a) == FAIL), false)) {
        return FAIL;
    }
    long b;
    if (__builtin_expect((enif_get_int64(env, argv[1], &b) == FAIL), false)) {
        return FAIL;
    }
    return enif_make_int64(env, (a + b));
  }
}
zacky1972 commented 5 years ago

I'll change the specification of ZEAM IR according to IR or ASTs using tuples and lists. However, it's troublesome so I'll implement #71, change the specification of ZEAM IR, and implement #70.

hisaway commented 4 years ago

@zacky1972 I designed ZEAM IR using Elixir Map .

original code:

const int fail = 0;
#define loop_vectorize_width 4

static ERL_NIF_TERM
map_plus_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
  size_t vec_l;
  double *vec_double;
  if (__builtin_expect((enif_get_double_vec_from_list(env, argv[0], &vec_double, &vec_l) == fail), false)) {
    return enif_make_badarg(env);
  }
#pragma clang loop vectorize_width(loop_vectorize_width)
  for(size_t i = 0; i < vec_l; i++) {
    vec_double[i] = vec_double[i] + 1;
  }
  return enif_make_list_from_double_vec(env, vec_double, vec_l);
}

ZEAM IR:

global_vars = %{
  fail: {:fail, [type: ["const", "int"]], 0}
},

macros = %{
  loop_vectorization: 4
}

nif_vars =  %{
  env:  {:env, [type: "ERL_NIF_ENV"], nil},
  argc: {:argc, [type: "int"], nil},
  argv: {:argv, [type: ["const", "ERL_NIF_TERM*"]], nil}
}

%{
  fucntions: "map_plus_1",
  arguments: [
    nif_vars[env], 
    nif_vars[argc], 
    nif_vars[argv]
  ],
  return_type: static_ERL_NIF_TERM
  do: %{
    vars: %{
      vec_l: {:vec_l, [type: "size_t"], nil},
      vec_double: {:vec_double, [type: "double*"], nil}
    },
    fucntion_calls: %{
      enif_get_double_vec_from_list_1: %{
        function: "enif_get_double_vec_from_list",
        arguments: [
          nif_vars[env], 
          nif_vars[argv, 0], 
          vars[vec_double], 
          vars[vec_l]
        ],
        return_type: "int",
      },
      enif_make_list_from_double_vec_1: %{
        funtion: "enif_make_list_from_double_vec",
        arguments: arguments: [
          nif_vars[env], 
          vars[vec_double], 
          vars[vec_l]
        ]
      }
    },

    controll: %{
      if1: %{
        expect: false,
        cond: %{
          function: "==",
          arguments: [
            fail,
            function_calls[:enif_get_double_vec_from_list_1]
          ],
          return_type: :bool
        },
        do: %{
          return: %{
            functions: enif_make_bad_arg,
            arguments: nif_vars[env]
          }
        }
      },
      for1: %{
        options: %{
          compiler: "clang",
          object: "loop",
          vectorize_width: loop_vectorize_width
        },
        increment: 1,
        count: vars[vec_l],
        do: %{
          fucntion: "=",
          arguments: [ vars[vec_double], %{
            fucntion: "+",
            arguments: [ vars[vec_double], 1],
            return_type: "double"
          }],
          return_type: "double"
        }
      },
      return: function_calls[:enif_make_list_from_double_vec_1]
    },

    flow: [ if1 |> for1 |> return ]
  }
}
zacky1972 commented 4 years ago

Single-map version:

%{
  global_vars: %{
    fail: {:fail, [type: ["const", "int"]], 0}
  },
  macros: %{
    loop_vectorization: 4
  },
  nif_vars: %{
    env:  {:env, [type: "ERL_NIF_ENV"], nil},
    argc: {:argc, [type: "int"], nil},
    argv: {:argv, [type: ["const", "ERL_NIF_TERM*"]], nil}
  },
  func_map_plus_1: %{
    fucntions: "map_plus_1",
    arguments: [
      "nif_vars[env]", 
      "nif_vars[argc]", 
      "nif_vars[argv]"
    ],
    return_type: :static_ERL_NIF_TERM,
    do: %{
      vars: %{
        vec_l: {:vec_l, [type: "size_t"], nil},
        vec_double: {:vec_double, [type: "double*"], nil}
      },
      fucntion_calls: %{
        enif_get_double_vec_from_list_1: %{
          function: "enif_get_double_vec_from_list",
          arguments: [
            "nif_vars[env]", 
            "nif_vars[argv, 0]", 
            "vars[vec_double]", 
            "vars[vec_l]"
          ],
          return_type: "int",
        },
        enif_make_list_from_double_vec_1: %{
          funtion: "enif_make_list_from_double_vec",
          arguments: [
            "nif_vars[env]", 
            "vars[vec_double]", 
            "vars[vec_l]"
          ]
        }
      },
      controll: %{
        if1: %{
          expect: false,
          cond: %{
            function: "==",
            arguments: [
              "fail",
              "function_calls[enif_get_double_vec_from_list_1]"
            ],
            return_type: :bool
          },
          do: %{
            return: %{
              functions: "enif_make_bad_arg",
              arguments: "nif_vars[env]"
            }
          }
        },
        for1: %{
          options: %{
            compiler: "clang",
            object: "loop",
            vectorize_width: "loop_vectorize_width"
          },
          increment: 1,
          count: "vars[vec_l]",
          do: %{
            fucntion: "=",
            arguments: [ "vars[vec_double]", %{
              fucntion: "+",
              arguments: [ "vars[vec_double]", 1],
              return_type: "double"
            }],
            return_type: "double"
          }
        },
        return: "function_calls[enif_make_list_from_double_vec_1]"
      },

      flow: [ "if1 |> for1 |> return" ]
    }
  }
}
zacky1972 commented 4 years ago

It needs to replace some maps into keyword lists because the order of them is important.

%{
  global_vars: [
    fail: {:fail, [type: ["const", "int"]], 0}
  ],
  macros: [
    loop_vectorization: 4
  ],
  nif_vars: %{
    env:  {:env, [type: "ERL_NIF_ENV"], nil},
    argc: {:argc, [type: "int"], nil},
    argv: {:argv, [type: ["const", "ERL_NIF_TERM*"]], nil}
  },
  func_map_plus_1: %{
    fucntions: "map_plus_1",
    arguments: [
      "nif_vars[env]", 
      "nif_vars[argc]", 
      "nif_vars[argv]"
    ],
    return_type: :static_ERL_NIF_TERM,
    do: %{
      vars: %{
        vec_l: {:vec_l, [type: "size_t"], nil},
        vec_double: {:vec_double, [type: "double*"], nil}
      },
      fucntion_calls: %{
        enif_get_double_vec_from_list_1: %{
          function: "enif_get_double_vec_from_list",
          arguments: [
            "nif_vars[env]", 
            "nif_vars[argv, 0]", 
            "vars[vec_double]", 
            "vars[vec_l]"
          ],
          return_type: "int",
        },
        enif_make_list_from_double_vec_1: %{
          funtion: "enif_make_list_from_double_vec",
          arguments: [
            "nif_vars[env]", 
            "vars[vec_double]", 
            "vars[vec_l]"
          ]
        }
      },
      controll: %{
        if1: %{
          expect: false,
          cond: %{
            function: "==",
            arguments: [
              "fail",
              "function_calls[enif_get_double_vec_from_list_1]"
            ],
            return_type: :bool
          },
          do: %{
            return: %{
              functions: "enif_make_bad_arg",
              arguments: "nif_vars[env]"
            }
          }
        },
        for1: %{
          options: %{
            compiler: "clang",
            object: "loop",
            vectorize_width: "loop_vectorize_width"
          },
          increment: 1,
          count: "vars[vec_l]",
          do: %{
            fucntion: "=",
            arguments: [ "vars[vec_double]", %{
              fucntion: "+",
              arguments: [ "vars[vec_double]", 1],
              return_type: "double"
            }],
            return_type: "double"
          }
        },
        return: "function_calls[enif_make_list_from_double_vec_1]"
      },

      flow: {:">|", [], [:if1, {:">|", [], [:for1, :return]}]}
    }
  }
}