Skip to content

Commit

Permalink
[Target] Add Target Parser for Arm(R) Cortex(R) M-Profile CPUs (apach…
Browse files Browse the repository at this point in the history
…e#12319)

This implements an initial Target Parser which uses the same logic as
the CMSIS-NN compiler flags to update the features and keys of the `c`
and `llvm` `Target`s.

Refactoring of the CMSIS-NN logic will be in a separate patch.
  • Loading branch information
Mousius authored and Mikael Sevenier committed Aug 12, 2022
1 parent ce9147f commit 13efb21
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ tvm_file_glob(GLOB_RECURSE COMPILER_SRCS
tvm_file_glob(GLOB CODEGEN_SRCS
src/target/*.cc
src/target/source/*.cc
src/target/parsers/*.cc
)

list(APPEND COMPILER_SRCS ${CODEGEN_SRCS})
Expand Down
5 changes: 5 additions & 0 deletions include/tvm/target/target_kind.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ namespace tvm {

class Target;

/*!
* \brief Map containing parsed features of a specific Target
*/
using TargetFeatures = Map<String, ObjectRef>;

/*!
* \brief TargetParser to apply on instantiation of a given TargetKind
*
Expand Down
2 changes: 1 addition & 1 deletion python/tvm/target/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def intel_graphics(model="unknown", options=None):
"imxrt10xx": ["-mcpu=cortex-m7"],
"mps2_an521": ["-mcpu=cortex-m33"],
"mps3_an547": ["-mcpu=cortex-m55"],
"nrf52840": ["-mcpu=cortex-m4"],
"nrf52840": ["-mcpu=cortex-m4+nodsp"],
"nrf5340dk": ["-mcpu=cortex-m33"],
"rp2040": ["-mcpu=cortex-m0"],
"sam3x8e": ["-mcpu=cortex-m3"],
Expand Down
41 changes: 41 additions & 0 deletions src/target/parsers/cpu.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "cpu.h"

#include <string>

#include "mprofile.h"

namespace tvm {
namespace target {
namespace parsers {
namespace cpu {

TargetJSON ParseTarget(TargetJSON target) {
if (mprofile::IsArch(target)) {
return mprofile::ParseTarget(target);
}

return target;
}

} // namespace cpu
} // namespace parsers
} // namespace target
} // namespace tvm
42 changes: 42 additions & 0 deletions src/target/parsers/cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*!
* \file tvm/target/parsers/cpu.h
* \brief Target Parser for CPU Target's
*/

#ifndef TVM_TARGET_PARSERS_CPU_H_
#define TVM_TARGET_PARSERS_CPU_H_

#include <tvm/target/target.h>

namespace tvm {
namespace target {
namespace parsers {
namespace cpu {

TargetJSON ParseTarget(TargetJSON target);

} // namespace cpu
} // namespace parsers
} // namespace target
} // namespace tvm

#endif // TVM_TARGET_PARSERS_CPU_H_
131 changes: 131 additions & 0 deletions src/target/parsers/mprofile.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*!
* \file tvm/target/parsers/mprofile.cc
* \brief Target Parser for Arm(R) Cortex(R) M-Profile CPUs
*/

#include "mprofile.h"

#include <string>

namespace tvm {
namespace target {
namespace parsers {
namespace mprofile {

const TargetFeatures kNoExt = {{"has_dsp", Bool(false)}, {"has_mve", Bool(false)}};
const TargetFeatures kHasDSP = {{"has_dsp", Bool(true)}, {"has_mve", Bool(false)}};
const TargetFeatures kHasMVE = {{"has_dsp", Bool(true)}, {"has_mve", Bool(true)}};

static const char* baseCPUs[] = {"cortex-m0", "cortex-m3"};
static const char* dspCPUs[] = {"cortex-m55", "cortex-m4", "cortex-m7", "cortex-m33",
"cortex-m35p"};
static const char* mveCPUs[] = {"cortex-m55"};

template <typename Container>
static inline bool MatchesCpu(Optional<String> mcpu, const Container& cpus) {
if (!mcpu) {
return false;
}
std::string mcpu_string = mcpu.value();
auto matches_cpu = [mcpu_string](const char* cpu) { return mcpu_string.find(cpu) == 0; };
return std::find_if(std::begin(cpus), std::end(cpus), matches_cpu) != std::end(cpus);
}

static inline bool HasFlag(String attr, std::string flag) {
std::string attr_str = attr;
return attr_str.find(flag) != std::string::npos;
}

static inline bool HasFlag(Optional<String> attr, std::string flag) {
if (!attr) {
return false;
}
return HasFlag(attr.value(), flag);
}

static inline bool HasFlag(Optional<Array<String>> attr, std::string flag) {
if (!attr) {
return false;
}
Array<String> attr_array = attr.value();

auto matching_attr = std::find_if(attr_array.begin(), attr_array.end(),
[flag](String attr_str) { return HasFlag(attr_str, flag); });
return matching_attr != attr_array.end();
}

bool IsArch(TargetJSON attrs) {
Optional<String> mcpu = Downcast<Optional<String>>(attrs.Get("mcpu"));
if (mcpu) {
bool matches_base = MatchesCpu(mcpu, baseCPUs);
bool matches_dsp = MatchesCpu(mcpu, dspCPUs);
bool matches_mve = MatchesCpu(mcpu, mveCPUs);
return matches_base || matches_mve || matches_dsp;
}
return false;
}

static TargetFeatures GetFeatures(TargetJSON target) {
Optional<String> mcpu = Downcast<Optional<String>>(target.Get("mcpu"));
Optional<Array<String>> mattr = Downcast<Optional<Array<String>>>(target.Get("mattr"));

bool nomve = HasFlag(mcpu, "+nomve") || HasFlag(mattr, "+nomve");
bool nodsp = HasFlag(mcpu, "+nodsp") || HasFlag(mattr, "+nodsp");

bool has_mve = MatchesCpu(mcpu, mveCPUs);
if (has_mve && !nomve && !nodsp) {
return kHasMVE;
}

bool has_dsp = MatchesCpu(mcpu, dspCPUs);
if (has_dsp && !nodsp) {
return kHasDSP;
}

return kNoExt;
}

static Array<String> MergeKeys(Optional<Array<String>> existing_keys) {
const String kExtraKey = "arm_cpu";

if (!existing_keys) {
return {kExtraKey};
}

Array<String> keys = existing_keys.value();
if (std::find(keys.begin(), keys.end(), kExtraKey) == keys.end()) {
keys.push_back(kExtraKey);
}
return keys;
}

TargetJSON ParseTarget(TargetJSON target) {
target.Set("features", GetFeatures(target));
target.Set("keys", MergeKeys(Downcast<Optional<Array<String>>>(target.Get("keys"))));

return target;
}

} // namespace mprofile
} // namespace parsers
} // namespace target
} // namespace tvm
43 changes: 43 additions & 0 deletions src/target/parsers/mprofile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*!
* \file tvm/target/parsers/mprofile.h
* \brief Target Parser for Arm(R) Cortex(R) M-Profile CPUs
*/

#ifndef TVM_TARGET_PARSERS_MPROFILE_H_
#define TVM_TARGET_PARSERS_MPROFILE_H_

#include <tvm/target/target.h>

namespace tvm {
namespace target {
namespace parsers {
namespace mprofile {

bool IsArch(TargetJSON target);
TargetJSON ParseTarget(TargetJSON target);

} // namespace mprofile
} // namespace parsers
} // namespace target
} // namespace tvm

#endif // TVM_TARGET_PARSERS_MPROFILE_H_
7 changes: 5 additions & 2 deletions src/target/target_kind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <algorithm>

#include "../node/attr_registry.h"
#include "./parsers/cpu.h"

namespace tvm {

Expand Down Expand Up @@ -281,7 +282,8 @@ TVM_REGISTER_TARGET_KIND("llvm", kDLCPU)
.set_default_keys({"cpu"})
// Force the external codegen kind attribute to be registered, even if no external
// codegen targets are enabled by the TVM build.
.set_attr<Bool>(tvm::attr::kIsExternalCodegen, Bool(false));
.set_attr<Bool>(tvm::attr::kIsExternalCodegen, Bool(false))
.set_target_parser(tvm::target::parsers::cpu::ParseTarget);

TVM_REGISTER_TARGET_KIND("c", kDLCPU)
.add_attr_option<Bool>("system-lib")
Expand All @@ -294,7 +296,8 @@ TVM_REGISTER_TARGET_KIND("c", kDLCPU)
.add_attr_option<Integer>("constants-byte-alignment")
.add_attr_option<Bool>("unpacked-api")
.add_attr_option<String>("interface-api")
.set_default_keys({"cpu"});
.set_default_keys({"cpu"})
.set_target_parser(tvm::target::parsers::cpu::ParseTarget);

TVM_REGISTER_TARGET_KIND("cuda", kDLCUDA)
.add_attr_option<String>("mcpu")
Expand Down
Loading

0 comments on commit 13efb21

Please sign in to comment.