diff --git a/pkg/codegen/prelude_get.go b/pkg/codegen/prelude_get.go index d449984..e255260 100644 --- a/pkg/codegen/prelude_get.go +++ b/pkg/codegen/prelude_get.go @@ -1,7 +1,9 @@ package codegen import ( + "github.com/llir/llvm/ir" "github.com/llir/llvm/ir/constant" + "github.com/llir/llvm/ir/enum" "github.com/llir/llvm/ir/types" "github.com/llir/llvm/ir/value" @@ -9,7 +11,7 @@ import ( ) func PreludeGet(ctx *Context, n *mTypes.Node) value.Value { - tyPtr, _ := n.IRValue.Type().(*types.PointerType) + tyPtr, isTyPtr := n.IRValue.Type().(*types.PointerType) tyStr, _ := tyPtr.ElemType.(*types.StructType) specifiedFields := ctx.prog.Declare.Type.Struct[tyStr.TypeName].Field[n.Next.Val] @@ -19,12 +21,49 @@ func PreludeGet(ctx *Context, n *mTypes.Node) value.Value { targetStructPtr := n.IRValue if structeType == nil || elemType == nil { - // Note: is it ok returing i32* anytime? return constant.NewNull(types.NewPointer(types.I32)) } - loadedStruct := ctx.block.NewLoad(structeType, targetStructPtr) - elemPtr := ctx.block.NewExtractValue(loadedStruct, i) + if !isTyPtr { + loadedStruct := ctx.block.NewLoad(structeType, targetStructPtr) + elemPtr := ctx.block.NewExtractValue(loadedStruct, i) + return elemPtr + } + + cond := ctx.block.NewICmp( + enum.IPredEQ, + targetStructPtr, + constant.NewNull(tyPtr), + ) + + nullBlock := ctx.NewBlock("get.null", n) + nonNullBlock := ctx.NewBlock("get.non.null", n) + mergeBlock := ctx.NewBlock("get.merge", n) + + ctx.block.NewCondBr(cond, nullBlock, nonNullBlock) + + nullBlock.NewBr(mergeBlock) + + loadedStruct := nonNullBlock.NewLoad(structeType, targetStructPtr) + elemPtr := nonNullBlock.NewExtractValue(loadedStruct, i) + nonNullBlock.NewBr(mergeBlock) + + ctx.block = mergeBlock + + t, isElemPtr := elemPtr.Type().(*types.PointerType) + if !isElemPtr { + loadedStruct := ctx.block.NewLoad(structeType, targetStructPtr) + elemPtr := ctx.block.NewExtractValue(loadedStruct, i) + return elemPtr + } + + result := mergeBlock.NewPhi( + []*ir.Incoming{ + {X: constant.NewNull(t), Pred: nullBlock}, + {X: elemPtr, Pred: nonNullBlock}, + }..., + ) - return elemPtr + ctx.block = mergeBlock + return result } diff --git a/script/test-full.sh b/script/test-full.sh index 3aa5d04..25895a2 100755 --- a/script/test-full.sh +++ b/script/test-full.sh @@ -291,6 +291,7 @@ testexec(){ assertexec '(defschema Person {:name :: string :age :: int :isMale :: bool :height :: double :v :: [int]}) (def main :: int (fn [] (let [node :: Person {:age 20 :name "richard" :height 5.8 :v [4 3 2]}] (prn (get node :isMale)))))' "false\\\n" assertexec '(defschema Person {:name :: string :age :: int :isMale :: bool :height :: double :v :: [int]}) (def main :: int (fn [] (let [node :: Person {:age 20 :name "richard" :isMale true :v [4 3 2]}] (prn (get node :height)))))' "0\\\n" assertexec '(defschema Person {:name :: string :age :: int :isMale :: bool :height :: double :v :: [int]}) (def main :: int (fn [] (let [node :: Person {:age 20 :name "richard" :isMale true :height 5.8 }] (prn (get node :v)))))' "nil\\\n" + assertexec '(defschema Country {:name ::string}) (defschema Person {:name ::string :country ::Country}) (def main ::int (fn [] (let [co ::Country {:name "deutsch"} person ::Person {:name "jimmy"}] (prn (get (get person :country) :name)))))' "nil\\\n" ############# # Prelude functions