/*
* Copyright (c) 2016, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.nodes.intrinsics.c;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.llvm.nodes.func.LLVMNativeCallUtils;
import com.oracle.truffle.llvm.nodes.intrinsics.llvm.LLVMIntrinsic;
import com.oracle.truffle.llvm.runtime.LLVMAddress;
import com.oracle.truffle.llvm.runtime.global.LLVMGlobalVariable;
import com.oracle.truffle.llvm.runtime.interop.ToLLVMNode;
import com.oracle.truffle.llvm.runtime.memory.LLVMMemory;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
public final class LLVMTruffleOnlyIntrinsics {
private LLVMTruffleOnlyIntrinsics() {
}
public abstract static class LLVMTruffleOnlyI64Intrinsic extends LLVMIntrinsic {
@Child private Node foreignExecute;
protected final TruffleObject nativeFunction;
public LLVMTruffleOnlyI64Intrinsic(TruffleObject nativeSymbol, String signature, int arity) {
nativeFunction = LLVMNativeCallUtils.bindNativeSymbol(nativeSymbol, signature);
foreignExecute = Message.createExecute(arity).createNode();
}
protected final long callNative(Object... args) {
try {
return (long) ForeignAccess.sendExecute(foreignExecute, nativeFunction, args);
} catch (InteropException e) {
throw new IllegalStateException(e);
}
}
}
public abstract static class LLVMTruffleOnlyI32Intrinsic extends LLVMIntrinsic {
@Child private Node foreignExecute;
protected final TruffleObject nativeFunction;
public LLVMTruffleOnlyI32Intrinsic(TruffleObject nativeSymbol, String signature, int arity) {
nativeFunction = LLVMNativeCallUtils.bindNativeSymbol(nativeSymbol, signature);
foreignExecute = Message.createExecute(arity).createNode();
}
protected final int callNative(Object... args) {
try {
return (int) ForeignAccess.sendExecute(foreignExecute, nativeFunction, args);
} catch (InteropException e) {
throw new IllegalStateException(e);
}
}
}
@NodeChild(type = LLVMExpressionNode.class)
public abstract static class LLVMStrlen extends LLVMTruffleOnlyI64Intrinsic {
protected static final long MAX_JAVA_LEN = 512;
public LLVMStrlen(TruffleObject nativeSymbol) {
super(nativeSymbol, "(POINTER):SINT64", 1);
}
@Specialization
public long executeIntrinsic(LLVMAddress string) {
return strlen(string.getVal());
}
@Specialization
public long executeIntrinsic(LLVMGlobalVariable string) {
return strlen(string.getNativeLocation().getVal());
}
@CompilationFinal private boolean inJava = true;
protected long strlen(long value) {
if (inJava) {
long s;
for (s = value; CompilerDirectives.injectBranchProbability(CompilerDirectives.LIKELY_PROBABILITY, LLVMMemory.getI8(s) != 0); s++) {
}
long result = s - value;
if (result > MAX_JAVA_LEN) {
CompilerDirectives.transferToInterpreterAndInvalidate();
inJava = false;
}
return result;
} else {
return callNative(value);
}
}
@Child private Node foreignHasSize = Message.HAS_SIZE.createNode();
@Child private Node foreignGetSize = Message.GET_SIZE.createNode();
@Child private ToLLVMNode toLLVM = ToLLVMNode.createNode(long.class);
@Specialization
public long executeIntrinsic(TruffleObject object) {
boolean hasSize = ForeignAccess.sendHasSize(foreignHasSize, object);
if (hasSize) {
Object strlen;
try {
strlen = ForeignAccess.sendGetSize(foreignGetSize, object);
long size = (long) toLLVM.executeWithTarget(strlen);
return size;
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new AssertionError(e);
}
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new AssertionError(object);
}
}
}
@NodeChildren({@NodeChild(type = LLVMExpressionNode.class), @NodeChild(type = LLVMExpressionNode.class)})
public abstract static class LLVMStrCmp extends LLVMTruffleOnlyI32Intrinsic {
public LLVMStrCmp(TruffleObject symbol) {
super(symbol, "(POINTER,POINTER):SINT32", 2);
}
@Specialization
public int executeIntrinsic(LLVMAddress str1, LLVMAddress str2) {
return callNative(str1.getVal(), str2.getVal());
}
@Specialization
public int executeIntrinsic(LLVMGlobalVariable str1, LLVMAddress str2) {
return callNative(str1.getNativeLocation().getVal(), str2.getVal());
}
@Specialization
public int executeIntrinsic(LLVMAddress str1, LLVMGlobalVariable str2) {
return callNative(str1.getVal(), str2.getNativeLocation().getVal());
}
@Specialization
public int executeIntrinsic(LLVMGlobalVariable str1, LLVMGlobalVariable str2) {
return callNative(str1.getNativeLocation().getVal(), str2.getNativeLocation().getVal());
}
@Child private Node readStr1 = Message.READ.createNode();
@Child private Node readStr2 = Message.READ.createNode();
@Child private Node getSize1 = Message.GET_SIZE.createNode();
@Child private Node getSize2 = Message.GET_SIZE.createNode();
@Child private ToLLVMNode toLLVMSize1 = ToLLVMNode.createNode(long.class);
@Child private ToLLVMNode toLLVMSize2 = ToLLVMNode.createNode(long.class);
@Child private ToLLVMNode toLLVM1 = ToLLVMNode.createNode(char.class);
@Child private ToLLVMNode toLLVM2 = ToLLVMNode.createNode(char.class);
@Specialization(limit = "20", guards = {"getSize1(str1) == size1", "getSize2(str2) == size2"})
@ExplodeLoop
public int executeIntrinsic(TruffleObject str1, TruffleObject str2, @Cached("getSize1(str1)") long size1, @Cached("getSize2(str2)") long size2) {
try {
int i;
for (i = 0; i < size1; i++) {
Object s1 = ForeignAccess.sendRead(readStr1, str1, i);
char c1 = (char) toLLVM1.executeWithTarget(s1);
if (i >= size2) {
return c1;
}
Object s2 = ForeignAccess.sendRead(readStr2, str2, i);
char c2 = (char) toLLVM2.executeWithTarget(s2);
if (c1 != c2) {
return c1 - c2;
}
}
if (i < size2) {
Object s2 = ForeignAccess.sendRead(readStr2, str2, i);
char c2 = (char) toLLVM2.executeWithTarget(s2);
return -c2;
} else {
return 0;
}
} catch (Exception e) {
throw new AssertionError(e);
}
}
protected long getSize2(TruffleObject str2) {
try {
return (long) toLLVMSize2.executeWithTarget(ForeignAccess.sendGetSize(getSize2, str2));
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException(e);
}
}
protected long getSize1(TruffleObject str1) {
try {
return (long) toLLVMSize1.executeWithTarget(ForeignAccess.sendGetSize(getSize1, str1));
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException(e);
}
}
@Specialization(guards = {"getSize2(str2) == size2"})
@ExplodeLoop
public int executeIntrinsic(LLVMAddress str1, TruffleObject str2, @Cached("getSize2(str2)") long size2) {
try {
char[] arr = new char[(int) size2];
for (int i = 0; i < size2; i++) {
Object s2 = ForeignAccess.sendRead(readStr2, str2, i);
char c2 = (char) toLLVM2.executeWithTarget(s2);
arr[i] = c2;
}
return compare(str1, arr);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static int compare(LLVMAddress str1, char[] arr) {
int i;
long ptr = str1.getVal();
for (i = 0; true; i++) {
char c1 = (char) LLVMMemory.getI8(ptr + i);
if (c1 == '\0') {
break;
}
if (i >= arr.length) {
return c1;
}
if (c1 != arr[i]) {
return c1 - arr[i];
}
}
if (i < arr.length) {
return -arr[i];
} else {
return 0;
}
}
}
}