Implement new naming scheme for --target.

Now targets are named like "<isa>-i<mask size>x<gang size>", e.g.
"sse4-i8x16", or "avx2-i32x16".

The old target names are still supported.
This commit is contained in:
Matt Pharr
2013-08-08 19:23:44 -07:00
parent 1d76f74b16
commit 0c5742b6f8
7 changed files with 163 additions and 100 deletions

View File

@@ -489,54 +489,72 @@ on which you're running ``ispc`` is used to determine the target CPU.
ispc foo.ispc -o foo.obj --cpu=corei7-avx
Finally, ``--target`` selects the target instruction set. The following
targets are currently supported:
Finally, ``--target`` selects the target instruction set. The target
string is of the form ``[ISA]-i[mask size]x[gang size]``. For example,
``--target=avx2-i32x16`` specifies a target with the AVX2 instruction set,
a mask size of 32 bits, and a gang size of 16.
=========== ========= =======================================
Target Gang Size Description
----------- --------- ---------------------------------------
avx 8 AVX (2010-2011 era Intel CPUs)
avx-x2 16 "Double-pumped" AVX target, running
twice as many program instances as the
native vector width.
avx1.1 8 AVX 1.1 target (2012 era "Ivybridge"
Intel CPUs).
avx1.1-x2 16 Double-pumped AVX 1.1 target.
avx2 8 AVX 2 target (2013- Intel "Haswell"
CPUs.)
avx2-x2 16 Double-pumped AVX 2 target.
neon-8 16 ARM NEON target, targeting computation
on 8-bit data types.
neon-16 8 ARM NEON target, targeting computation
on 16-bit data types.
neon-32 4 ARM NEON target, targeting computation
on 32-bit data types.
sse2 4 SSE2 (early 2000s era x86 CPUs).
sse2-x2 8 Double-pumped SSE2.
sse4 4 SSE4 (generally 2008-2010 Intel CPUs).
sse4-x2 8 Double-pumped SSE4.
sse4-8 16 SSE4 target targeting computation on
8-bit data types.
sse4-16 8 SSE4 target targeting computation on
16-bit data types.
=========== ========= =======================================
The following target ISAs are supported:
============ ==========================================
Target Description
------------ ------------------------------------------
avx, avx1 AVX (2010-2011 era Intel CPUs)
avx1.1 AVX 1.1 (2012 era "Ivybridge" Intel CPUs)
avx2 AVX 2 target (2013- Intel "Haswell" CPUs)
neon ARM NEON
sse2 SSE2 (early 2000s era x86 CPUs)
sse4 SSE4 (generally 2008-2010 Intel CPUs)
============ ==========================================
Consult your CPU's manual for specifics on which vector instruction set it
supports.
The mask size may be 8, 16, or 32 bits, though not all combinations of ISAs
and mask sizes are supported. For best performance, the best general
approach is to choose a mask size equal to the size of the most common
datatype in your programs. For example, if most of your computation is on
32-bit floating-point values, an ``i32`` target is appropriate. However,
if you're mostly doing computation on 8-bit images, ``i8`` is a better choice.
See `Basic Concepts: Program Instances and Gangs of Program Instances`_ for
more discussion of the "gang size" and its implications for program
execution.
instruction sets. (As general context, SSE2 was first introduced in
processors that shipped in 2001, SSE4 was introduced in 2007, and
processors with AVX were introduced in 2010, and AVX2 arrived in 2013.
Consult your CPU's
manual for specifics on which vector instruction set it supports.)
Running ``ispc --help`` and looking at the output for the ``--target``
option gives the most up-to-date documentation about which targets your
compiler binary supports.
The naming scheme for compilation targets changed in August 2013; the
following table shows the relationship between names in the old scheme and
in the new scheme:
============= ===========
Target Former Name
------------- -----------
avx1-i32x8 avx, avx1
avx1-i32x16 avx-x2
avx1.1-i32x8 avx1.1
avx1.1-i32x16 avx1.1-x2
avx2-i32x8 avx2
avx2-i32x16 avx2-x2
neon-8 n/a
neon-16 n/a
neon-32 n/a
sse2-i32x4 sse2
sse2-i32x8 sse2-x2
sse4-i32x4 sse4
sse4-i32x8 sse4-x2
sse4-i8x16 n/a
sse4-i16x8 n/a
============= ===========
By default, the target instruction set is chosen based on the most capable
one supported by the system on which you're running ``ispc``. You can
override this choice with the ``--target`` flag; for example, to select
Intel® SSE2, use ``--target=sse2``. (As with the other options in this
section, see the output of ``ispc --help`` for a full list of supported
targets.)
Intel® SSE2 with a 32-bit mask and 4 program instances in a gang, use
``--target=sse2-i32x4``. (As with the other options in this section, see
the output of ``ispc --help`` for a full list of supported targets.)
Generating Generic C++ Output
-----------------------------

116
ispc.cpp
View File

@@ -106,7 +106,7 @@ static void __cpuidex(int info[4], int level, int count) {
static const char *
lGetSystemISA() {
#ifdef __arm__
return "neon-32";
return "neon-i32x4";
#else
int info[4];
__cpuid(info, 1);
@@ -121,19 +121,19 @@ lGetSystemISA() {
int info2[4];
__cpuidex(info2, 7, 0);
if ((info2[1] & (1 << 5)) != 0)
return "avx2";
return "avx2-i32x8";
else
return "avx1.1";
return "avx1.1-i32x8";
}
// Regular AVX
return "avx";
return "avx-i32x8";
}
else if ((info[2] & (1 << 19)) != 0)
return "sse4";
return "sse4-i32x4";
else if ((info[3] & (1 << 26)) != 0)
return "sse2";
return "sse2-i32x4";
else {
fprintf(stderr, "Unable to detect supported SSE/AVX ISA. Exiting.\n");
Error(SourcePos(), "Unable to detect supported SSE/AVX ISA. Exiting.");
exit(1);
}
#endif
@@ -186,22 +186,22 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
// If a CPU was specified explicitly, try to pick the best
// possible ISA based on that.
if (!strcmp(cpu, "core-avx2"))
isa = "avx2";
isa = "avx2-i32x8";
#ifdef ISPC_ARM_ENABLED
else if (!strcmp(cpu, "cortex-a9") ||
!strcmp(cpu, "cortex-a15"))
isa = "neon-32";
isa = "neon-i32x4";
#endif
else if (!strcmp(cpu, "core-avx-i"))
isa = "avx1.1";
isa = "avx1.1-i32x8";
else if (!strcmp(cpu, "sandybridge") ||
!strcmp(cpu, "corei7-avx"))
isa = "avx";
isa = "avx-i32x8";
else if (!strcmp(cpu, "corei7") ||
!strcmp(cpu, "penryn"))
isa = "sse4";
isa = "sse4-i32x4";
else
isa = "sse2";
isa = "sse2-i32x4";
Warning(SourcePos(), "No --target specified on command-line. "
"Using ISA \"%s\" based on specified CPU \"%s\".", isa,
cpu);
@@ -211,7 +211,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
// supports.
isa = lGetSystemISA();
Warning(SourcePos(), "No --target specified on command-line. "
"Using system ISA \"%s\".", isa);
"Using default system target \"%s\".", isa);
}
}
@@ -241,8 +241,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
}
}
if (foundCPU == false) {
fprintf(stderr, "Error: CPU type \"%s\" unknown. Supported CPUs: "
"%s.\n", cpu, SupportedTargetCPUs().c_str());
Error(SourcePos(), "Error: CPU type \"%s\" unknown. Supported CPUs: "
"%s.", cpu, SupportedCPUs().c_str());
return;
}
}
@@ -283,7 +283,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
}
// Check default LLVM generated targets
if (!strcasecmp(isa, "sse2")) {
if (!strcasecmp(isa, "sse2") ||
!strcasecmp(isa, "sse2-i32x4")) {
this->m_isa = Target::SSE2;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 4;
@@ -291,7 +292,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "sse2-x2")) {
else if (!strcasecmp(isa, "sse2-x2") ||
!strcasecmp(isa, "sse2-i32x8")) {
this->m_isa = Target::SSE2;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 8;
@@ -299,7 +301,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "sse4")) {
else if (!strcasecmp(isa, "sse4") ||
!strcasecmp(isa, "sse4-i32x4")) {
this->m_isa = Target::SSE4;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 4;
@@ -308,7 +311,9 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "sse4x2") || !strcasecmp(isa, "sse4-x2")) {
else if (!strcasecmp(isa, "sse4x2") ||
!strcasecmp(isa, "sse4-x2") ||
!strcasecmp(isa, "sse4-i32x8")) {
this->m_isa = Target::SSE4;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 8;
@@ -316,7 +321,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "sse4-8")) {
else if (!strcasecmp(isa, "sse4-i8x16")) {
this->m_isa = Target::SSE4;
this->m_nativeVectorWidth = 16;
this->m_vectorWidth = 16;
@@ -324,7 +329,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 8;
}
else if (!strcasecmp(isa, "sse4-16")) {
else if (!strcasecmp(isa, "sse4-i16x8")) {
this->m_isa = Target::SSE4;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -332,7 +337,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 16;
}
else if (!strcasecmp(isa, "generic-4")) {
else if (!strcasecmp(isa, "generic-4") ||
!strcasecmp(isa, "generic-x4")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 4;
@@ -342,7 +348,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasTranscendentals = true;
this->m_hasGather = this->m_hasScatter = true;
}
else if (!strcasecmp(isa, "generic-8")) {
else if (!strcasecmp(isa, "generic-8") ||
!strcasecmp(isa, "generic-x8")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -352,7 +359,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasTranscendentals = true;
this->m_hasGather = this->m_hasScatter = true;
}
else if (!strcasecmp(isa, "generic-16")) {
else if (!strcasecmp(isa, "generic-16") ||
!strcasecmp(isa, "generic-x16")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 16;
this->m_vectorWidth = 16;
@@ -362,7 +370,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasTranscendentals = true;
this->m_hasGather = this->m_hasScatter = true;
}
else if (!strcasecmp(isa, "generic-32")) {
else if (!strcasecmp(isa, "generic-32") ||
!strcasecmp(isa, "generic-x32")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 32;
this->m_vectorWidth = 32;
@@ -372,7 +381,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasTranscendentals = true;
this->m_hasGather = this->m_hasScatter = true;
}
else if (!strcasecmp(isa, "generic-64")) {
else if (!strcasecmp(isa, "generic-64") ||
!strcasecmp(isa, "generic-x64")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 64;
this->m_vectorWidth = 64;
@@ -382,14 +392,17 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasTranscendentals = true;
this->m_hasGather = this->m_hasScatter = true;
}
else if (!strcasecmp(isa, "generic-1")) {
else if (!strcasecmp(isa, "generic-1") ||
!strcasecmp(isa, "generic-x1")) {
this->m_isa = Target::GENERIC;
this->m_nativeVectorWidth = 1;
this->m_vectorWidth = 1;
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "avx") || !strcasecmp(isa, "avx1")) {
else if (!strcasecmp(isa, "avx") ||
!strcasecmp(isa, "avx1") ||
!strcasecmp(isa, "avx1-i32x8")) {
this->m_isa = Target::AVX;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -397,7 +410,9 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "avx-x2") || !strcasecmp(isa, "avx1-x2")) {
else if (!strcasecmp(isa, "avx-x2") ||
!strcasecmp(isa, "avx1-x2") ||
!strcasecmp(isa, "avx1-i32x16")) {
this->m_isa = Target::AVX;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 16;
@@ -405,7 +420,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 32;
}
else if (!strcasecmp(isa, "avx1.1")) {
else if (!strcasecmp(isa, "avx1.1") ||
!strcasecmp(isa, "avx1.1-i32x8")) {
this->m_isa = Target::AVX11;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -418,7 +434,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasRand = true;
#endif
}
else if (!strcasecmp(isa, "avx1.1-x2")) {
else if (!strcasecmp(isa, "avx1.1-x2") ||
!strcasecmp(isa, "avx1.1-i32x16")) {
this->m_isa = Target::AVX11;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 16;
@@ -431,7 +448,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasRand = true;
#endif
}
else if (!strcasecmp(isa, "avx2")) {
else if (!strcasecmp(isa, "avx2") ||
!strcasecmp(isa, "avx2-i32x8")) {
this->m_isa = Target::AVX2;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -449,7 +467,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_hasGather = true;
#endif
}
else if (!strcasecmp(isa, "avx2-x2")) {
else if (!strcasecmp(isa, "avx2-x2") ||
!strcasecmp(isa, "avx2-i32x16")) {
this->m_isa = Target::AVX2;
this->m_nativeVectorWidth = 16;
this->m_vectorWidth = 16;
@@ -468,7 +487,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
#endif
}
#ifdef ISPC_ARM_ENABLED
else if (!strcasecmp(isa, "neon-8")) {
else if (!strcasecmp(isa, "neon-i8x16")) {
this->m_isa = Target::NEON8;
this->m_nativeVectorWidth = 16;
this->m_vectorWidth = 16;
@@ -477,7 +496,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 8;
}
else if (!strcasecmp(isa, "neon-16")) {
else if (!strcasecmp(isa, "neon-i16x8")) {
this->m_isa = Target::NEON16;
this->m_nativeVectorWidth = 8;
this->m_vectorWidth = 8;
@@ -486,7 +505,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
this->m_maskingIsFree = false;
this->m_maskBitCount = 16;
}
else if (!strcasecmp(isa, "neon-32") || !strcasecmp(isa, "neon")) {
else if (!strcasecmp(isa, "neon") ||
!strcasecmp(isa, "neon-i32x4")) {
this->m_isa = Target::NEON32;
this->m_nativeVectorWidth = 4;
this->m_vectorWidth = 4;
@@ -497,8 +517,8 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
}
#endif
else {
fprintf(stderr, "Target ISA \"%s\" is unknown. Choices are: %s\n",
isa, SupportedTargetISAs());
Error(SourcePos(), "Target \"%s\" is unknown. Choices are: %s.",
isa, SupportedTargets());
error = true;
}
@@ -592,7 +612,7 @@ Target::Target(const char *arch, const char *cpu, const char *isa, bool pic) :
std::string
Target::SupportedTargetCPUs() {
Target::SupportedCPUs() {
std::string ret;
int count = sizeof(supportedCPUs) / sizeof(supportedCPUs[0]);
for (int i = 0; i < count; ++i) {
@@ -605,7 +625,7 @@ Target::SupportedTargetCPUs() {
const char *
Target::SupportedTargetArchs() {
Target::SupportedArchs() {
return
#ifdef ISPC_ARM_ENABLED
"arm, "
@@ -615,14 +635,18 @@ Target::SupportedTargetArchs() {
const char *
Target::SupportedTargetISAs() {
Target::SupportedTargets() {
return
#ifdef ISPC_ARM_ENABLED
"neon-8, neon-16, neon-32, "
"neon-i8x16, neon-16x8, neon-32x4, "
#endif
"sse2, sse2-x2, sse4, sse4-8, sse4-16, sse4-x2, "
"avx, avx-x2, avx1.1, avx1.1-x2, avx2, avx2-x2, "
"generic-1, generic-4, generic-8, generic-16, generic-32";
"sse2-i32x4, sse2-i32x8, "
"sse4-i32x4, sse4-i32x8, sse4-i16x8, sse4-i8x16, "
"avx1-i32x8, avx1-i32x16, "
"avx1.1-i32x8, avx1.1-i32x16, "
"avx2-i32x8, avx2-i32x16, "
"generic-x1, generic-x4, generic-x8, generic-x16, "
"generic-x32, generic-x64";
}

12
ispc.h
View File

@@ -192,16 +192,16 @@ public:
Target(const char *arch, const char *cpu, const char *isa, bool pic);
/** Returns a comma-delimited string giving the names of the currently
supported target ISAs. */
static const char *SupportedTargetISAs();
supported compilation targets. */
static const char *SupportedTargets();
/** Returns a comma-delimited string giving the names of the currently
supported target CPUs. */
static std::string SupportedTargetCPUs();
supported CPUs. */
static std::string SupportedCPUs();
/** Returns a comma-delimited string giving the names of the currently
supported target architectures. */
static const char *SupportedTargetArchs();
supported architectures. */
static const char *SupportedArchs();
/** Returns a triple string specifying the target architecture, vendor,
and environment. */

View File

@@ -85,13 +85,16 @@ usage(int ret) {
printf(" \t\taddressing calculations are done by default, even\n");
printf(" \t\ton 64-bit target architectures.)\n");
printf(" [--arch={%s}]\t\tSelect target architecture\n",
Target::SupportedTargetArchs());
Target::SupportedArchs());
printf(" [--c++-include-file=<name>]\t\tSpecify name of file to emit in #include statement in generated C++ code.\n");
#ifndef ISPC_IS_WINDOWS
printf(" [--colored-output]\t\tAlways use terminal colors in error/warning messages.\n");
#endif
printf(" [--cpu=<cpu>]\t\t\tSelect target CPU type\n");
printf(" <cpu>={%s}\n", Target::SupportedTargetCPUs().c_str());
printf(" ");
char cpuHelp[2048];
sprintf(cpuHelp, "[--cpu=<cpu>]\t\t\tSelect target CPU type\n<cpu>={%s}\n",
Target::SupportedCPUs().c_str());
PrintWithWordBreaks(cpuHelp, 16, TerminalWidth(), stdout);
printf(" [-D<foo>]\t\t\t\t#define given value when running preprocessor\n");
printf(" [--dev-stub <filename>]\t\tEmit device-side offload stub functions to file\n");
printf(" [--emit-asm]\t\t\tGenerate assembly language file as output\n");
@@ -126,7 +129,11 @@ usage(int ret) {
printf(" [--pic]\t\t\t\tGenerate position-independent code\n");
#endif // !ISPC_IS_WINDOWS
printf(" [--quiet]\t\t\t\tSuppress all output\n");
printf(" [--target=<isa>]\t\t\tSelect target ISA. <isa>={%s}\n", Target::SupportedTargetISAs());
printf(" ");
char targetHelp[2048];
sprintf(targetHelp, "[--target=<t>]\t\t\tSelect target ISA and width.\n"
"<t>={%s}", Target::SupportedTargets());
PrintWithWordBreaks(targetHelp, 24, TerminalWidth(), stdout);
printf(" [--version]\t\t\t\tPrint ispc version\n");
printf(" [--werror]\t\t\t\tTreat warnings as errors\n");
printf(" [--woff]\t\t\t\tDisable warnings\n");

View File

@@ -37,7 +37,7 @@ parser.add_option("-g", "--generics-include", dest="include_file", help="Filenam
parser.add_option("-f", "--ispc-flags", dest="ispc_flags", help="Additional flags for ispc (-g, -O1, ...)",
default="")
parser.add_option('-t', '--target', dest='target',
help='Set compilation target (neon8, neon16, neon32, sse2, sse2-x2, sse4, sse4-x2, sse4-8, sse4-16, avx, avx-x2, generic-4, generic-8, generic-16, generic-32)',
help='Set compilation target (sse2-i32x4, sse2-i32x8, sse4-i32x4, sse4-i32x8, sse4-i16x8, sse4-i8x16, avx1-i32x8, avx1-i32x16, avx1.1-i32x8, avx1.1-i32x16, avx2-i32x8, avx2-i32x16, generic-x1, generic-x4, generic-x8, generic-x16, generic-x32, generic-x64)',
default="sse4")
parser.add_option('-a', '--arch', dest='arch',
help='Set architecture (arm, x86, x86-64)',

View File

@@ -79,8 +79,8 @@
compiler under a debuffer; in this case, just return a reasonable
default.
*/
static int
lTerminalWidth() {
int
TerminalWidth() {
if (g->disableLineWrap)
return 1<<30;
@@ -228,8 +228,8 @@ lFindIndent(int numColons, const char *buf) {
/** Print the given string to the given FILE, assuming the given output
column width. Break words as needed to avoid words spilling past the
last column. */
static void
lPrintWithWordBreaks(const char *buf, int indent, int columnWidth, FILE *out) {
void
PrintWithWordBreaks(const char *buf, int indent, int columnWidth, FILE *out) {
#ifdef ISPC_IS_WINDOWS
fputs(buf, out);
fputs("\n", out);
@@ -375,7 +375,7 @@ lPrint(const char *type, bool isError, SourcePos p, const char *fmt,
return;
printed.insert(formattedBuf);
lPrintWithWordBreaks(formattedBuf, indent, lTerminalWidth(), stderr);
PrintWithWordBreaks(formattedBuf, indent, TerminalWidth(), stderr);
lPrintFileLineContext(p);
free(errorBuf);

14
util.h
View File

@@ -156,4 +156,18 @@ void GetDirectoryAndFileName(const std::string &currentDir,
bool VerifyDataLayoutCompatibility(const std::string &module_dl,
const std::string &lib_dl);
/** Print the given string to the given FILE, assuming the given output
column width. Break words as needed to avoid words spilling past the
last column. */
void PrintWithWordBreaks(const char *buf, int indent, int columnWidth,
FILE *out);
/** Returns the width of the terminal where the compiler is running.
Finding this out may fail in a variety of reasonable situations (piping
compiler output to 'less', redirecting output to a file, running the
compiler under a debuffer; in this case, just return a reasonable
default.
*/
int TerminalWidth();
#endif // ISPC_UTIL_H