[svn r126] String switch is now implemented.

A few other fixes.
This commit is contained in:
Tomas Lindquist Olsen
2007-11-27 09:19:07 +01:00
parent ea362d8402
commit 0665531549
6 changed files with 572 additions and 16 deletions

View File

@@ -526,9 +526,52 @@ void ThrowStatement::toIR(IRState* p)
//////////////////////////////////////////////////////////////////////////////
// used to build the sorted list of cases
struct Case : Object
{
StringExp* str;
size_t index;
Case(StringExp* s, size_t i) {
str = s;
index = i;
}
int compare(Object *obj) {
Case* c2 = (Case*)obj;
return str->compare(c2->str);
}
};
static llvm::Value* call_string_switch_runtime(llvm::GlobalVariable* table, Expression* e)
{
Type* dt = DtoDType(e->type);
Type* dtnext = DtoDType(dt->next);
TY ty = dtnext->ty;
const char* fname;
if (ty == Tchar) {
fname = "_d_switch_string";
}
else if (ty == Twchar) {
fname = "_d_switch_ustring";
}
else if (ty == Tdchar) {
fname = "_d_switch_dstring";
}
else {
assert(0 && "not char/wchar/dchar");
}
llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, fname);
std::vector<llvm::Value*> args;
args.push_back(table);
args.push_back(e->toElem(gIR)->getRVal());
return gIR->ir->CreateCall(fn, args.begin(), args.end(), "tmp");
}
void SwitchStatement::toIR(IRState* p)
{
Logger::println("SwitchStatement::toIR(): %s", toChars());
Logger::println("SwitchStatement::toIR()");
LOG_SCOPE;
llvm::BasicBlock* oldend = gIR->scopeend();
@@ -537,24 +580,32 @@ void SwitchStatement::toIR(IRState* p)
typedef std::pair<llvm::BasicBlock*, std::vector<llvm::ConstantInt*> > CasePair;
std::vector<CasePair> vcases;
std::vector<Statement*> vbodies;
Array caseArray;
for (int i=0; i<cases->dim; ++i)
{
CaseStatement* cs = (CaseStatement*)cases->data[i];
// create the case bb with a nice label
std::string lblname("case"+std::string(cs->exp->toChars()));
std::string lblname("case");
llvm::BasicBlock* bb = new llvm::BasicBlock(lblname, p->topfunc(), oldend);
std::vector<llvm::ConstantInt*> tmp;
CaseStatement* last;
bool first = true;
do {
// get the case value
DValue* e = cs->exp->toElem(p);
DConstValue* ce = e->isConst();
assert(ce);
llvm::ConstantInt* ec = isaConstantInt(ce->c);
assert(ec);
tmp.push_back(ec);
// integral case
if (cs->exp->type->isintegral()) {
llvm::Constant* c = cs->exp->toConstElem(p);
tmp.push_back(isaConstantInt(c));
}
// string case
else {
assert(cs->exp->op == TOKstring);
// for string switches this is unfortunately necessary or there will be duplicates in the list
if (first) {
caseArray.push(new Case((StringExp*)cs->exp, i));
first = false;
}
}
last = cs;
}
while (cs = cs->statement->isCaseStatement());
@@ -563,6 +614,42 @@ void SwitchStatement::toIR(IRState* p)
vbodies.push_back(last->statement);
}
// string switch?
llvm::GlobalVariable* switchTable = 0;
if (!condition->type->isintegral())
{
// first sort it
caseArray.sort();
// iterate and add indices to cases
std::vector<llvm::Constant*> inits;
for (size_t i=0; i<caseArray.dim; ++i)
{
Case* c = (Case*)caseArray.data[i];
vcases[c->index].second.push_back(DtoConstUint(i));
inits.push_back(c->str->toConstElem(p));
}
// build static array for ptr or final array
const llvm::Type* elemTy = DtoType(condition->type);
const llvm::ArrayType* arrTy = llvm::ArrayType::get(elemTy, inits.size());
llvm::Constant* arrInit = llvm::ConstantArray::get(arrTy, inits);
llvm::GlobalVariable* arr = new llvm::GlobalVariable(arrTy, true, llvm::GlobalValue::InternalLinkage, arrInit, "string_switch_table_data", gIR->module);
const llvm::Type* elemPtrTy = llvm::PointerType::get(elemTy);
llvm::Constant* arrPtr = llvm::ConstantExpr::getBitCast(arr, elemPtrTy);
// build the static table
std::vector<const llvm::Type*> types;
types.push_back(DtoSize_t());
types.push_back(elemPtrTy);
const llvm::StructType* sTy = llvm::StructType::get(types);
std::vector<llvm::Constant*> sinits;
sinits.push_back(DtoConstSize_t(inits.size()));
sinits.push_back(arrPtr);
llvm::Constant* sInit = llvm::ConstantStruct::get(sTy, sinits);
switchTable = new llvm::GlobalVariable(sTy, true, llvm::GlobalValue::InternalLinkage, sInit, "string_switch_table", gIR->module);
}
// default
llvm::BasicBlock* defbb = 0;
if (!hasNoDefault) {
@@ -573,9 +660,17 @@ void SwitchStatement::toIR(IRState* p)
llvm::BasicBlock* endbb = new llvm::BasicBlock("switchend", p->topfunc(), oldend);
// condition var
DValue* cond = condition->toElem(p);
llvm::SwitchInst* si = new llvm::SwitchInst(cond->getRVal(), defbb ? defbb : endbb, cases->dim, p->scopebb());
delete cond;
llvm::Value* condVal;
// integral switch
if (condition->type->isintegral()) {
DValue* cond = condition->toElem(p);
condVal = cond->getRVal();
}
// string switch
else {
condVal = call_string_switch_runtime(switchTable, condition);
}
llvm::SwitchInst* si = new llvm::SwitchInst(condVal, defbb ? defbb : endbb, cases->dim, p->scopebb());
// add the cases
size_t n = vcases.size();

View File

@@ -92,11 +92,18 @@ DValue* DeclarationExp::toElem(IRState* p)
Logger::println("AliasDeclaration - no work");
// do nothing
}
// enum
else if (EnumDeclaration* e = declaration->isEnumDeclaration())
{
Logger::println("EnumDeclaration - no work");
// do nothing
}
// class
else if (ClassDeclaration* e = declaration->isClassDeclaration())
{
Logger::println("ClassDeclaration");
DtoForceConstInitDsymbol(e);
}
// unsupported declaration
else
{
@@ -364,7 +371,7 @@ DValue* StringExp::toElem(IRState* p)
Type* dtype = DtoDType(type);
Type* cty = DtoDType(dtype->next);
const llvm::Type* ct = DtoType(dtype->next);
const llvm::Type* ct = DtoType(cty);
//printf("ct = %s\n", type->next->toChars());
const llvm::ArrayType* at = llvm::ArrayType::get(ct,len+1);

View File

@@ -153,6 +153,7 @@ lphobos/internal/contract.d
lphobos/internal/mem.d
lphobos/internal/objectimpl.d
lphobos/internal/qsort2.d
lphobos/internal/switch.d
lphobos/llvm
lphobos/llvm/intrinsic.d
lphobos/llvm/va_list.d
@@ -327,6 +328,7 @@ test/bug74.d
test/bug75.d
test/bug76.d
test/bug77.d
test/bug78.d
test/bug8.d
test/bug9.d
test/c.d
@@ -432,6 +434,7 @@ test/structs6.d
test/structs7.d
test/switch1.d
test/switch2.d
test/switch3.d
test/sync1.d
test/templ1.d
test/templ2.d

View File

@@ -29,10 +29,11 @@ echo "compiling object/interface casting runtime support"
llvmdc internal/cast.d -c -odobj || exit 1
llvm-link -f -o=../lib/llvmdcore.bc obj/cast.bc ../lib/llvmdcore.bc || exit 1
echo "compiling string foreach runtime support"
echo "compiling string foreach/switch runtime support"
llvmdc internal/aApply.d -c -odobj || exit 1
llvmdc internal/aApplyR.d -c -odobj || exit 1
llvm-link -f -o=../lib/llvmdcore.bc obj/aApply.bc obj/aApplyR.bc ../lib/llvmdcore.bc || exit 1
llvmdc internal/switch.d -c -odobj || exit 1
llvm-link -f -o=../lib/llvmdcore.bc obj/aApply.bc obj/aApplyR.bc obj/switch.bc ../lib/llvmdcore.bc || exit 1
echo "compiling array runtime support"
llvmdc internal/qsort2.d -c -odobj || exit 1

426
lphobos/internal/switch.d Normal file
View File

@@ -0,0 +1,426 @@
/*
* Copyright (C) 2004-2007 by Digital Mars, www.digitalmars.com
* Written by Walter Bright
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, in both source and binary form, subject to the following
* restrictions:
*
* o The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* o Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
* o This notice may not be removed or altered from any source
* distribution.
*/
import std.c.stdio;
import std.c.string;
import std.string;
/******************************************************
* Support for switch statements switching on strings.
* Input:
* table[] sorted array of strings generated by compiler
* ca string to look up in table
* Output:
* result index of match in table[]
* -1 if not in table
*/
extern (C):
int _d_switch_string(char[][] table, char[] ca)
in
{
//printf("in _d_switch_string()\n");
assert(table.length >= 0);
assert(ca.length >= 0);
// Make sure table[] is sorted correctly
int j;
for (j = 1; j < table.length; j++)
{
int len1 = table[j - 1].length;
int len2 = table[j].length;
assert(len1 <= len2);
if (len1 == len2)
{
int ci;
ci = memcmp(table[j - 1].ptr, table[j].ptr, len1);
assert(ci < 0); // ci==0 means a duplicate
}
}
}
out (result)
{
int i;
int cj;
//printf("out _d_switch_string()\n");
if (result == -1)
{
// Not found
for (i = 0; i < table.length; i++)
{
if (table[i].length == ca.length)
{ cj = memcmp(table[i].ptr, ca.ptr, ca.length);
assert(cj != 0);
}
}
}
else
{
assert(0 <= result && result < table.length);
for (i = 0; 1; i++)
{
assert(i < table.length);
if (table[i].length == ca.length)
{
cj = memcmp(table[i].ptr, ca.ptr, ca.length);
if (cj == 0)
{
assert(i == result);
break;
}
}
}
}
}
body
{
//printf("body _d_switch_string(%.*s)\n", ca);
int low;
int high;
int mid;
int c;
char[] pca;
low = 0;
high = table.length;
version (none)
{
// Print table
printf("ca[] = '%s'\n", cast(char *)ca);
for (mid = 0; mid < high; mid++)
{
pca = table[mid];
printf("table[%d] = %d, '%.*s'\n", mid, pca.length, pca);
}
}
if (high &&
ca.length >= table[0].length &&
ca.length <= table[high - 1].length)
{
// Looking for 0 length string, which would only be at the beginning
if (ca.length == 0)
return 0;
char c1 = ca[0];
// Do binary search
while (low < high)
{
mid = (low + high) >> 1;
pca = table[mid];
c = ca.length - pca.length;
if (c == 0)
{
c = cast(ubyte)c1 - cast(ubyte)pca[0];
if (c == 0)
{
c = memcmp(ca.ptr, pca.ptr, ca.length);
if (c == 0)
{ //printf("found %d\n", mid);
return mid;
}
}
}
if (c < 0)
{
high = mid;
}
else
{
low = mid + 1;
}
}
}
//printf("not found\n");
return -1; // not found
}
unittest
{
switch (cast(char []) "c")
{
case "coo":
default:
break;
}
}
/**********************************
* Same thing, but for wide chars.
*/
int _d_switch_ustring(wchar[][] table, wchar[] ca)
in
{
//printf("in _d_switch_ustring()\n");
assert(table.length >= 0);
assert(ca.length >= 0);
// Make sure table[] is sorted correctly
int j;
for (j = 1; j < table.length; j++)
{
int len1 = table[j - 1].length;
int len2 = table[j].length;
assert(len1 <= len2);
if (len1 == len2)
{
int c;
c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * wchar.sizeof);
assert(c < 0); // c==0 means a duplicate
}
}
}
out (result)
{
int i;
int c;
//printf("out _d_switch_string()\n");
if (result == -1)
{
// Not found
for (i = 0; i < table.length; i++)
{
if (table[i].length == ca.length)
{ c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof);
assert(c != 0);
}
}
}
else
{
assert(0 <= result && result < table.length);
for (i = 0; 1; i++)
{
assert(i < table.length);
if (table[i].length == ca.length)
{
c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof);
if (c == 0)
{
assert(i == result);
break;
}
}
}
}
}
body
{
//printf("body _d_switch_ustring()\n");
int low;
int high;
int mid;
int c;
wchar[] pca;
low = 0;
high = table.length;
/*
// Print table
wprintf("ca[] = '%.*s'\n", ca);
for (mid = 0; mid < high; mid++)
{
pca = table[mid];
wprintf("table[%d] = %d, '%.*s'\n", mid, pca.length, pca);
}
*/
// Do binary search
while (low < high)
{
mid = (low + high) >> 1;
pca = table[mid];
c = ca.length - pca.length;
if (c == 0)
{
c = memcmp(ca.ptr, pca.ptr, ca.length * wchar.sizeof);
if (c == 0)
{ //printf("found %d\n", mid);
return mid;
}
}
if (c < 0)
{
high = mid;
}
else
{
low = mid + 1;
}
}
//printf("not found\n");
return -1; // not found
}
unittest
{
switch (cast(wchar []) "c")
{
case "coo":
default:
break;
}
}
/**********************************
* Same thing, but for wide chars.
*/
int _d_switch_dstring(dchar[][] table, dchar[] ca)
in
{
//printf("in _d_switch_dstring()\n");
assert(table.length >= 0);
assert(ca.length >= 0);
// Make sure table[] is sorted correctly
int j;
for (j = 1; j < table.length; j++)
{
int len1 = table[j - 1].length;
int len2 = table[j].length;
assert(len1 <= len2);
if (len1 == len2)
{
int c;
c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * dchar.sizeof);
assert(c < 0); // c==0 means a duplicate
}
}
}
out (result)
{
int i;
int c;
//printf("out _d_switch_string()\n");
if (result == -1)
{
// Not found
for (i = 0; i < table.length; i++)
{
if (table[i].length == ca.length)
{ c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof);
assert(c != 0);
}
}
}
else
{
assert(0 <= result && result < table.length);
for (i = 0; 1; i++)
{
assert(i < table.length);
if (table[i].length == ca.length)
{
c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof);
if (c == 0)
{
assert(i == result);
break;
}
}
}
}
}
body
{
//printf("body _d_switch_ustring()\n");
int low;
int high;
int mid;
int c;
dchar[] pca;
low = 0;
high = table.length;
/*
// Print table
wprintf("ca[] = '%.*s'\n", ca);
for (mid = 0; mid < high; mid++)
{
pca = table[mid];
wprintf("table[%d] = %d, '%.*s'\n", mid, pca.length, pca);
}
*/
// Do binary search
while (low < high)
{
mid = (low + high) >> 1;
pca = table[mid];
c = ca.length - pca.length;
if (c == 0)
{
c = memcmp(ca.ptr, pca.ptr, ca.length * dchar.sizeof);
if (c == 0)
{ //printf("found %d\n", mid);
return mid;
}
}
if (c < 0)
{
high = mid;
}
else
{
low = mid + 1;
}
}
//printf("not found\n");
return -1; // not found
}
unittest
{
switch (cast(dchar []) "c")
{
case "coo":
default:
break;
}
}

24
test/switch3.d Normal file
View File

@@ -0,0 +1,24 @@
module switch3;
void main()
{
char[] str = "hello";
int i;
switch(str)
{
case "world":
i = 1;
assert(0);
case "hello":
i = 2;
break;
case "a","b","c":
i = 3;
assert(0);
default:
i = 4;
assert(0);
}
assert(i == 2);
printf("SUCCESS\n");
}